首先是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. Go语言最佳实践——异常和错误

    Go语言将错误和异常两者区分对待. 1.Go语言中处理错误的惯用法是将错误以函数或者方法最后一个返回值的形式将其返回,并总是在调用它的地方检查返回的错误值. 2.对于“不可能发生的事情”称为异常,可使 ...

  2. C# System.Threading.Timer 定时器

    前提: 需要引入  System.Threading: 描述: 在很多时间我们都需要进行延迟执行,或是定时执行一些指定业务,这个时候使用 Timer 是最合适的,而且 Timer 是Cpu 级别处理对 ...

  3. web3部署智能合约碰到的一个奇怪问题

    都是gasLimit惹的祸 解决一个奇怪问题Error: Number can only safely store up to 53 bits 原来好好的node endpointtest.js ,结 ...

  4. 「BZOJ 2342」「SHOI 2011」双倍回文「Manacher」

    题意 记\(s_R\)为\(s\)翻转后的串,求一个串最长的形如\(ss_Rss_R\)的子串长度 题解 这有一个复杂度明显\(O(n)\)的做法,思路来自网上某篇博客 一个双倍回文串肯定当且仅当本身 ...

  5. kali linux之webshell

    webacoo(web backdoor cookie) 类终端的shell 编码通信内容通过cookie头传输,隐蔽性较强 cm:base64编码的命令 cn:服务器用于返回数据的cookie头的名 ...

  6. shell-脚本-作用是发现前10个最占用磁盘空间的文件

    #!/bin/bash## 这个脚本的作用是发现前10个最占用磁盘空间的文件################定义这个脚本的全区变量##################################C ...

  7. ulimit -n 查看可以打开的最大文件描述符的数量

    具体ulimit命令参考 https://www.cnblogs.com/wangkangluo1/archive/2012/06/06/2537677.html

  8. Udp 网络字节序

    1.网络上的数据是一个字节一个字节的串行传递的. 2.字节序,规定,在内存里存储时,低字节在前称为小端,高字节在前称为大端,(现在主流系统都是小端的) 3.网络字节序,如果先传高字节,则是大端传输:如 ...

  9. Python3之Memcache使用

    简介 Memcached是一个高性能的分布式内存对象缓存系统,用于动态WEB应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态,数据库网站的速度.Memcached ...

  10. Reviewing notes 2.1 of Mathematical analysis

    Chapter2 Numerical sequence and function Cartesian product set If S and T are sets,then the cartesia ...