首先是ABPZero的第三方登录模块,通过调用第三方的登录接口返回用户信息,再交给ABP的登录验证模块去执行对应的登录注册。

涉及的数据库表主要是这两个表,AbpUsers存储了用户信息,AbpUserLogins存储了登录方式,第三方登录的信息就是存储在这里的

主要是四个字段 LoginProvider ProviderKey TenantId UserId

        登录提供器   用户唯一Id  对应的租户Id和用户Id

首先需要编写一个LoginProvider,代码如下

using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Abp.AspNetZeroCore.Web.Authentication.External;
using Castle.Core.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Schema; namespace Web.Authentication.External
{
public class WechatMiniProgramAuthProviderApi : ExternalAuthProviderApiBase
{
/// <summary>
/// 微信小程序
/// </summary>
public const string ProviderName = "WeChatMiniProgram";
WeChatMiniProgramOptions _options;
JSchema schema = JSchema.Parse(JsonConvert.SerializeObject(new WeChatSession()));
JSchema accessSchema = JSchema.Parse(JsonConvert.SerializeObject(new { AccessCode="", Name="" }));
const string url = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&grant_type=authorization_code&js_code={2}";
private readonly IExternalAuthConfiguration _externalAuthConfiguration;
private readonly ILogger logger;
public WechatMiniProgramAuthProviderApi(IExternalAuthConfiguration externalAuthConfiguration, ILogger logger)
{
_externalAuthConfiguration = externalAuthConfiguration;
var r = externalAuthConfiguration.Providers.First(p => p.Name == ProviderName);
_options = new WeChatMiniProgramOptions
{
AppId = r.ClientId,
Secret = r.ClientSecret
}; this.logger = logger;
} public async override Task<ExternalAuthUserInfo> GetUserInfo(string accessCode)//因为需要获取微信放进User.Name
{                                              //所以accessCode需要多一个Name
JObject jObject = JObject.Parse(accessCode);              //就长这样 {"AccessCode":"xxxxxxxx", "Name":"Sam"}
if (!jObject.IsValid(accessSchema))                    //所以用Jobect解析出来
{
throw new Abp.UI.UserFriendlyException("accessCode Json inVaild");
}
accessCode = jObject["AccessCode"].ToString();
string name = jObject["Name"].ToString();
//string rowData = jObject["RowData"].ToString();
var result = await GetOpenId(accessCode);                 //获取到OpenId,说明accessCode是对的 实际上应该再通过OpenId解密数据后获取NickName的,偷懒了...
//logger.Info("OpenId:" + result.Openid);                 //获取不到则在方法内部抛出异常,不会返回用户信息,也就不会执行之后的登陆注册操作
var t = result == null ? new ExternalAuthUserInfo() : new ExternalAuthUserInfo//
{
EmailAddress = result.Openid + "@test.cn",
Surname = name,
ProviderKey = result.Openid,//唯一
Provider = ProviderName,
Name = name
};
return t; } private async Task<WeChatSession> GetOpenId(string code)
{
string geturl = string.Format(url, _options.AppId, _options.Secret, code);
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var result = await client.GetAsync(geturl);
if (result.IsSuccessStatusCode)
{ //{"errcode":40163,"errmsg":"code been used, hints: [ req_id: tWZH6a0160th19 ]"}
string re = await result.Content.ReadAsStringAsync();//{"session_key":"eafmjK9FYzCVpqPSo\/FBsQ==","openid":"oUigJ47QGkNOOXUjHkii5LyJbukw"}
var jo = JObject.Parse(re);
if (jo.IsValid(schema))
{
var m = JsonConvert.DeserializeObject<WeChatSession>(re);
return m;
}
}
return null ;
}
} class WeChatSession
{
public string Openid { get; set; }
public string Session_key { get; set; }
}   /// <summary>
    /// 微信小程序配置选项
    /// </summary>
    public class WeChatMiniProgramOptions
    {
        /// <summary>
        /// AppId
        /// </summary>
        public string AppId { get; set; }
        /// <summary>
        /// 密钥
        /// </summary>
        public string Secret { get; set; }
    }
}

然后在appsettings.json的Authentication节点中配置微信小程序的开启和appid 密钥的配置

然后在WebHostModule.cs中判断是否开启,执行配置(如果是MVC项目则是 项目名+WebMVCModule.cs)

因为默认的ProviderKey要求同一个登陆器下的同一用唯一,但是微信小程序里只有OpenId能做到用户唯一,OpenId又不能放到网络里传输,因此就需要修改一下默认的方式

注释掉 WebCore项目中Controller中TokenAuthController的GetExternalUserInfo方法中的判断调用接口传入ProviderKey和提供器返回用户信息ProviderKey一致。

这样Login表中的ProviderKey就会存储第一次登录时传入的accessCode。

最后只需要在LoginAsync方法传入的Login对象时传入获取到的用户模型的ProviderKey就能通过验证了,为了 不破坏原有的登录接口,我重写了一个WeChatAuthenticate方法。(这里的和上面的GetExternalUserInfo方法都是在WebCore项目Controller下的TokenAuthController.cs文件里)

注意 标红的代码,这里要传入的是从GetUserInfo获取到的ProviderKey,而不是从model里获取的ProviderKey,否则由于传入的和数据库里存储的不匹配导致登录失败而认为是新的账户,执行注册,最后发现用户名冲突而注册失败。

  [HttpPost]
public async Task<ExternalAuthenticateResultModel> WeChatAuthenticate([FromBody] ExternalAuthenticateModel model)
{
var externalUser = await GetExternalUserInfo(model);
//Logger.Info($"用户模型:{Newtonsoft.Json.JsonConvert.SerializeObject(externalUser)}");
//Logger.Debug(Newtonsoft.Json.JsonConvert.SerializeObject(new UserLoginInfo(model.AuthProvider, externalUser.ProviderKey, model.AuthProvider) ) + GetTenancyNameOrNull());
var loginResult = await _logInManager.LoginAsync(new UserLoginInfo(model.AuthProvider, externalUser.ProviderKey, model.AuthProvider), GetTenancyNameOrNull());
//Logger.Debug(loginResult.Result.ToString());
switch (loginResult.Result)
{
case AbpLoginResultType.Success:
{
var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity)); var returnUrl = model.ReturnUrl; if (model.SingleSignIn.HasValue && model.SingleSignIn.Value && loginResult.Result == AbpLoginResultType.Success)
{
loginResult.User.SetSignInToken();
returnUrl = AddSingleSignInParametersToReturnUrl(model.ReturnUrl, loginResult.User.SignInToken, loginResult.User.Id, loginResult.User.TenantId);
}
return new ExternalAuthenticateResultModel
{
AccessToken = accessToken,
EncryptedAccessToken = GetEncrpyedAccessToken(accessToken),
ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds,
ReturnUrl = returnUrl
};
}
case AbpLoginResultType.UnknownExternalLogin:
{
var newUser = await RegisterExternalUserAsync(externalUser);
if (!newUser.IsActive)
{
return new ExternalAuthenticateResultModel
{
WaitingForActivation = true
};
} //Try to login again with newly registered user!
loginResult = await _logInManager.LoginAsync(new UserLoginInfo(model.AuthProvider, externalUser.ProviderKey, model.AuthProvider), GetTenancyNameOrNull());
if (loginResult.Result != AbpLoginResultType.Success)
{
throw _abpLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(
loginResult.Result,
model.ProviderKey,
GetTenancyNameOrNull()
);
} var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
return new ExternalAuthenticateResultModel
{
AccessToken = accessToken,
EncryptedAccessToken = GetEncrpyedAccessToken(accessToken),
ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds
};
}
default:
{
throw _abpLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(
loginResult.Result,
model.ProviderKey,
GetTenancyNameOrNull()
);
}
}
}

最后只需要调用WeChatExternalAuthenticate接口就可以了

需要注意的是这几个参数需要全部传入,哪怕传入为空。providerKey和ProviderAccessCode都传入微信小程序提供的accessCode。

这样就会返回accessToken了,调用接口时Hearder加上Bearer  accessToken就可以了

ABP Zero集成微信小程序登录的更多相关文章

  1. 补充ABP Zero集成微信小程序登陆的BUG修复部分

    感谢园友 @turingguo 发布的 https://www.cnblogs.com/turingguo/p/9019026.html  文章,详细介绍了ABP Zero集成微信小程序登陆的实现过程 ...

  2. 微信小程序登录方案

    微信小程序登录方案 登录程序 app.js 调用wx.login获取code 将code作为参数请求自己业务登录接口获取session_key 存储session_key 如果有回调执行回调 App( ...

  3. 微信小程序登录,获取code,获取openid,获取session_key

    微信小程序登录 wx.login(Object object) 调用接口获取登录凭证(code).通过凭证进而换取用户登录态信息,包括用户的唯一标识(openid)及本次登录的会话密钥(session ...

  4. 基于Shiro,JWT实现微信小程序登录完整例子

    小程序官方流程图如下,官方地址 : https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html ...

  5. 微信小程序登录JAVA后台

    代码地址如下:http://www.demodashi.com/demo/12736.html 登录流程时序登录流程时序 具体的登录说明查看 小程序官方API 项目的结构图: springboot项目 ...

  6. 微信小程序登录对接Django后端实现JWT方式验证登录

    先上效果图 点击授权按钮后可以显示部分资料和头像,点击修改资料可以修改部分资料. 流程 1.使用微信小程序登录和获取用户信息Api接口 2.把Api获取的用户资料和code发送给django后端 3. ...

  7. 全栈项目|小书架|微信小程序-登录及token鉴权

    小程序登录 之前也写过微信小程序登录的相关文章: 微信小程序~新版授权用户登录例子 微信小程序-携带Token无感知登录的网络请求方案 微信小程序开通云开发并利用云函数获取Openid 也可以通过官方 ...

  8. Flask与微信小程序登录(后端)

    开发微信小程序时,接入小程序的授权登录可以快速实现用户注册登录的步骤,是快速建立用户体系的重要一步.这篇文章将介绍 python + flask + 微信小程序实现用户快速注册登录方案(本文主要进行后 ...

  9. Taro -- 微信小程序登录

    Taro微信小程序登录 1.调用Taro.login()获取登录凭证code: 2.调用Taro.request()将code传到服务器: 3.服务器端调用微信登录校验接口(appid+appsecr ...

随机推荐

  1. css属性position的运用

    随着web标准的规范化,网页的布局也随之千变万化.各种复杂漂亮有创意的页面布局冲 击这人们的视野,相比以前的table布局那就不是一等级的事儿.这个很大一部分功劳是css 样式的引入.而这个多样性布局 ...

  2. 编写高质量代码改善C#程序的157个建议——建议25:谨慎集合属性的可写操作

    建议25:谨慎集合属性的可写操作 如果类型的属性中有集合属性,那么应该保证属性对象是由类型本身产生的.如果将属性设置为可写,则会增加抛出异常的几率.一般情况下,如果集合属性没有值,则它返回的Count ...

  3. PostBack

    PostBack 字面意义 Post提交 Back回来. 提交回来. 1. AutoPostBack 服务器控件需要设置 AutoPostBack="true" 后才会提交服务器. ...

  4. arcconf工具操作手册V1.0

    arcconf工具操作手册 1.1.1  arcconf工具初始化和去初始化硬盘 [命令功能] PMC阵列卡系统下初始化硬盘,可以将raw盘状态变成ready状态,以便进一步组建raid和设置热备盘: ...

  5. IE11下使用fixed定位时鼠标滚动不平滑

    很久不用IE了,近期做兼容性测试发现一个fixed定位的问题,当元素使用fixed定位时,其应该不随页面滚动,在chrome/firefox/edge下都很完美,元素完全不动,但是使用IE11时,如果 ...

  6. VS Code 运行html文件

    用VS Code编写html文件,想在VS Code中直接打开运行,配置如下: 配置tasks.json 打开VS Code,点击"终端",选择"配置任务". ...

  7. WebService 天气预报webservice接口

    WebService  天气预报webservice接口 地址:http://www.webxml.com.cn/WebServices/WeatherWebService.asmx 常用接口: 1. ...

  8. [译] 关于 SPA,你需要掌握的 4 层 (1)

    此文已由作者张威授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 我们从头来构建一个 React 的应用程序,探究领域.存储.应用服务和视图这四层 每个成功的项目都需要一个清晰 ...

  9. 文件操作 - 整体操作&文件搜索

    文件操作 - 整体操作 1.touch 作用:创建普通文件 格式:touch file1 [file2] 2.cp 作用:拷贝文件 格式:cp 源文件 目标文件 3.rm 作用:删除文件 格式:rm ...

  10. ionic中文教程[来自皓眸大前端]

    做前端的同学有福了,学完比较热火的angular,你就可以开始动手做静态的WebApp了,这是多么幸福的一件事啊.静态的WebApp,你可以做任何的Demo,甚至可以做一些通关小游戏这个先不谈.做完了 ...