ABP Zero集成微信小程序登录
首先是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集成微信小程序登录的更多相关文章
- 补充ABP Zero集成微信小程序登陆的BUG修复部分
感谢园友 @turingguo 发布的 https://www.cnblogs.com/turingguo/p/9019026.html 文章,详细介绍了ABP Zero集成微信小程序登陆的实现过程 ...
- 微信小程序登录方案
微信小程序登录方案 登录程序 app.js 调用wx.login获取code 将code作为参数请求自己业务登录接口获取session_key 存储session_key 如果有回调执行回调 App( ...
- 微信小程序登录,获取code,获取openid,获取session_key
微信小程序登录 wx.login(Object object) 调用接口获取登录凭证(code).通过凭证进而换取用户登录态信息,包括用户的唯一标识(openid)及本次登录的会话密钥(session ...
- 基于Shiro,JWT实现微信小程序登录完整例子
小程序官方流程图如下,官方地址 : https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html ...
- 微信小程序登录JAVA后台
代码地址如下:http://www.demodashi.com/demo/12736.html 登录流程时序登录流程时序 具体的登录说明查看 小程序官方API 项目的结构图: springboot项目 ...
- 微信小程序登录对接Django后端实现JWT方式验证登录
先上效果图 点击授权按钮后可以显示部分资料和头像,点击修改资料可以修改部分资料. 流程 1.使用微信小程序登录和获取用户信息Api接口 2.把Api获取的用户资料和code发送给django后端 3. ...
- 全栈项目|小书架|微信小程序-登录及token鉴权
小程序登录 之前也写过微信小程序登录的相关文章: 微信小程序~新版授权用户登录例子 微信小程序-携带Token无感知登录的网络请求方案 微信小程序开通云开发并利用云函数获取Openid 也可以通过官方 ...
- Flask与微信小程序登录(后端)
开发微信小程序时,接入小程序的授权登录可以快速实现用户注册登录的步骤,是快速建立用户体系的重要一步.这篇文章将介绍 python + flask + 微信小程序实现用户快速注册登录方案(本文主要进行后 ...
- Taro -- 微信小程序登录
Taro微信小程序登录 1.调用Taro.login()获取登录凭证code: 2.调用Taro.request()将code传到服务器: 3.服务器端调用微信登录校验接口(appid+appsecr ...
随机推荐
- xen创建pvm和hvm的过程
these are the basic steps of installing domU with xen-tools in ubuntu13.04 64bit in xen4.3 you can a ...
- 福大软工1816|K班—alpha冲刺
Part.1 开篇 队名:彳艮彳亍团队 组长博客:戳我进入 作业博客:班级博客本次作业的链接 Part.2 成员汇报 组员1(组长)柯奇豪 过去两天完成了哪些任务 了解前端方面的相关内容,便于后续对进 ...
- 快速入手Web幻灯片制作
在线幻灯片 使用markdown可以快速的写出优美的文档,接下来我介绍一些简单的语法,快速的用浏览器制作幻灯片. 最基本使用格式 <!DOCTYPE html> <html> ...
- springMVC工作原理趣味解析
springMVC 涉及的人有: 1:浏览器 2:DispatherServlet 3:Handler 4:HandlerAdapter ...
- 求整数数组(长度为n),出现大于2/n次数的数字
条件:时间复杂度是O(n),空间复杂度是O(1) 方法1:标记法 , , , , , , , , , , , , , }; int len = arr.Length; int[] c = new in ...
- logback-记录日志
一:根节点<configuration>包含的属性: scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true. scanPeriod: 设置监测配 ...
- whisper简介
以太坊系列之二十 以太坊中的基础应用whisper 以太坊系列之二十 以太坊中的基础应用whisper 1 whisper介绍 2 whisper rpc模块 3 whisper中的消息 4 消息的加 ...
- 模态显示PresentModalViewController
1.主要用途 弹出模态ViewController是IOS变成中很有用的一个技术,UIKit提供的一些专门用于模态显示的ViewController,如UIImagePickerController等 ...
- kali linux之DNS,NTP放大攻击
DNS放大: 产生大流量的攻击方法-----单机的带宽优势,巨大的单机数量形成的流量汇聚,利用协议特性实现放大效果的流量 DNS协议放大效果----查询请求流量小,但响应流量可能非常巨大(dig AN ...
- 【bzoj2437】[Noi2011]兔兔与蛋蛋 二分图最大匹配+博弈论
Description Input 输入的第一行包含两个正整数 n.m. 接下来 n行描述初始棋盘.其中第i 行包含 m个字符,每个字符都是大写英文字母"X".大写英文字母&quo ...