首先是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。
最后只需要调用ExternalAuthenticate接口就可以了
需要注意的是这几个参数需要全部传入,哪怕传入为空。providerKey和ProviderAccessCode都传入微信小程序提供的accessCode。
这样就会返回accessToken了,调用接口时Hearder加上Bearer accessToken就可以了