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 ...
随机推荐
- HDU 3362 Fix (状压DP)
题意:题目给出n(n <= 18)个点的二维坐标,并说明某些点是被固定了的,其余则没固定,要求添加一些边,使得还没被固定的点变成固定的, 要求总长度最短. 析:由于这个 n 最大才是18,比较小 ...
- 设计模式10: Facade 外观模式(结构型模式)
Facade 外观模式(结构型模式) 系统的复杂度 假设我们要开发一个坦克模式系统用于模拟坦克车在各种作战环境中的行为,其中坦克系统由引擎.控制器.车轮.车身等各个子系统构成. internal cl ...
- Diameter协议摘要
---------选择同学整理文档 1. 协议概述 Diameter协议主要为应用程序提供认证.鉴权.计费框架,即AAA,并支持本地AAA和漫游场景下的AAA. 1.1. 特点介绍 以前的AAA ...
- HTML 5+CSS 3网页设计经典范例 (李俊民,黄盛奎) 随书光盘
<html 5+css 3网页设计经典范例(附cd光盘1张)>共分为18章,涵盖了html 5和css3中各方面的技术知识.主要内容包括html 5概述.html 5与html 4的区别. ...
- asp.net 中input radio checked 无效
把Jq代码中的$(...).attr("checked",true) 换成$(...).prop("checked",true) ,
- Canvas保存为图片
public static void GenerateCanvas(string imgSaveName, int canvasWidth, int canvasHeight, string imgD ...
- SharePoint 2013报错之“指定的文件不是有效的电子表格或者没有包含要导入的数据”
当你尝试用SharePoint 2013中的“导入电子表格”功能时,可能会遇到报错“指定的文件不是有效的电子表格或者没有包含要导入的数据” 解决方法:只需要将你的SharePoint网址添加到浏览器的 ...
- 使用VS Code编写Markdown文件
VS Code默认支持Markdown文件文件格式,这里介绍两个比较实用的功能,后续有新发现,可以持续更新. 实时预览 顾名思义,实时编辑,实时预览解析效果. 在VS Code扩展中搜索"M ...
- 分享我访问google的方法
对于程序员来说,有很多技术问题还是通过互联网搜索来解决的.所以百度.google对于我们是多少的重要.但是GOOGLE现在无法访问了.怎么办呢.以下是我访问google的方法. 首先自己制作了一个简单 ...
- FastDFS分布式⽂文件系统
FastDFS分布式⽂文件系统 1. 什么是FastDFS FastDFS 是⽤用 c 语⾔言编写的⼀一款开源的分布式⽂文件系统.FastDFS 为互联⽹网量量身定制, 充分考虑了了冗余备份.负载均 ...