热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

基于IdentityServer的系统对接微信公众号

业务需求公司有两个业务系统,A和B,AB用户之间属于多对一的关系,数据库里面也就是两张表,A表有个外键指向B。现在需要实现以

业务需求

公司有两个业务系统,A和B,AB用户之间属于多对一的关系,数据库里面也就是两张表,A表有个外键指向B。现在需要实现以下几个功能。

  1. A用户扫描B的二维码,填写相关的注册信息,注册完成之后自动属于B。也就是表A的外加字段指向B。

  2. 老用户和微信openid绑定。

  3. 用户在公众号里面自动登录。

项目结构

公司项目基于.net core 2.1 + Vue,

后端有以下几个子系统:

  1. 基于IdentityServer4 的asp.net mvc,简称account 项目,配了域名account.xxx.com

  2. 两个业务系统api。A和B。分别域名配置aapi.xxx.com 和bapi.xxx.com

  3. 其他。。

前端有以下几个系统,都是基于Vue的SPA:

  1. A业务系统,域名a.xxx.com

  2. B业务系统,域名b.xxx.com

  3. 其他。

登录这块的逻辑实现方式是类似的。都是基于IdentityModel/oidc-client-js

简单介绍一下IdentityServer这个东西。

用户登录A或B系统,就是调用A和B对应的webapi,webapi配置了自己的验证服务器是account服务器,account验证未通过,前端就得到401状态码,通过oidc-client-js的内部方法引导用户进行登录。跳转account的页面,用户输入用户名密码,登录成功,account服务器判断是A or B过来的登录请求,带上token回跳到配置的对应页面。业务系统再次用获取到的token请求api,调用成功。用一个图来说。

640?wx_fmt=png

实现方法

用了盛派微信sdk,特别感谢大佬的贡献。

推荐一下微信沙箱环境,项目做完下来除了"无法在测试的公众号里面推送小程序消息”无法实现之外(因为推送的需要公众号和小程序有一个绑定关系),其他都ok。

因为版本的关系,account系统升级了asp.net core 2.2。

先实现上面第一个需求

这里用到微信里面生成带参数的二维码功能。B系统创建了用户之后,生成一个对应的guid,然后把这个guid作为参数,调用sdk就能得到二维码的url。


var qrRstTicketRst = await QrCodeApi.CreateAsync(weixinSetting.WeixinAppId, 30, 100000,
QrCode_ActionName.QR_LIMIT_STR_SCENE, sceneId);

var url = QrCodeApi.GetShowQrCodeUrl(qrRstTicketRst.ticket);

这里我们项目中用到的是永久二维码,虽然这个二维码上限10W个,我们业务系统B用户不会超过那么多。

B用户展示二维码给A用户,A用户扫描,根据文档:

如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。

如果用户已经关注公众号,在用户扫描后会自动进入会话,微信也会将带场景值扫描事件推送给开发者。

触发代码里面分别对应的是OnEvent_SubscribeRequest 和OnEvent_ScanRequest,两个方法里面的代码基本上是一样的。RequestMessageEvent_Scan.EventKey可以得到上面的guid值。Subscribe事件里面得到的EventKey会比Scan的多一个qrscene前缀,处理的时候要注意一点。两个方法参数都能通过FromUserName获取到扫描的用户的openId,然后在这个方法里面返回一个带参数(A的openId,和B的guid)的注册链接,A用户注册的时候就提交了这两个参数,后台就能拿到。

顺道说一句,公众号里面用户每次操作只能被动返回一条消息。如要主动推送,需要用模板消息的方式。

实现第二个需求

对于老用户,这里需要一个账号绑定的功能。也就是业务系统的账号和openId做一个关联。绑定的关键在于这个如何获取这个openId,这里有两种方式。

  1. 用户点击公众号的菜单,后端获取到这个事件,在OnEvent_ClickRequest中,判断RequestMessageEvent_Click.EventKey==xxx,返回一个带openId的绑定页面的链接给用户。比如/bind?openId=xxx,用户点击这个链接,系统引导用户登录,然后点击绑定按钮,实现绑定。

  2. 基于微信网页授权,这个在自动登录里面也用到了,所以下面解释。

实现第三个需求

系统中用户和微信的openId已经绑定,所以,只要知道每次访问页面的openId就应该能实现自动登录。openId是通过微信网页授权的方式获取到。流程可以看文档。简单来说,先拿code,再换token,同时拿到openId。实现步骤分以下几步。

  • 添加一个公众号菜单,type是view,也就是点了之后会打开一个页面,页面地址直接用获取code的url.

{
"type": "view",
"name": "登录A",
"url":"https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxb66259f2a353&redirect_uri=http%3A%2F%2Faccount.xxx.cn%2Fweixincallback%2Fcallback&response_type=code&scope=snsapi_base&state=spa.A#wechat_redirect"
}

url中的state参数会和code一起返回给设置的redirectUrl,这个可以用来给我们在account登录中心判断是需要登录A还是B,以便最后回跳到对应的业务页面。

这里在沙箱配置跳转域名的时候注意一下,只要写域名就好。

640?wx_fmt=png

  • 打开页面,需要用户点允许授权。通过之后浏览器会把code 和 state参数带这get请求redirect_url

  • callback页面的逻辑

callback 接收code和state两个参数。

public async Task Callback(string code, string state){...}

用这个code调用sdk里面的api获取token,同时可以拿到openid。

var tokenResult = await OAuthApi.GetAccessTokenAsync(AppId, AppSecret, code);
if (tokenResult.errcode != ReturnCode.请求成功)
{
throw new BizException("获取微信用户信息失败");
}
var openId = tokenResult.openid;

通过open获取用户信息。

var userInfo = await userService.GetByOpenId(openId);

然后调用HttpContext.SignInAsync登录。

public static async Task SignInAsync(this HttpContext context, string subject, string name, AuthenticationProperties properties, params Claim[] claims)
{
var clock = context.GetClock();
var user = new IdentityServerUser(subject)
{
DisplayName = name,
AdditionalClaims = claims,
AuthenticationTime = clock.UtcNow.UtcDateTime
};
await context.SignInAsync(user, properties);
}

HttpContext是当前请求的上下文。

subject可以理解为用户的标识。

name可以理解是用户显示的名字。

AuthenticationProperties是此次认证的一些配置,比如有效时长之类的。

Claim可以理解为这个subject带的一些属性。

await HttpContext.SignInAsync(userMobile, userName, props, claims);

调用完之后就登录成功。

然后通过带来的state参数判断需要跳转的client。

var client = await clientStore.FindClientByIdAsync(state);
return Redirect($"{client.PostLogoutRedirectUris.FirstOrDefault()}?logined=true");

这里带一个logined=true参数,用来给client做一些逻辑。

总结

首先要感谢的肯定是盛派微信sdk的contributors,没有他们系统对接起来应该会慢很多。

然后我想说,IdentityServer是个好东西,现在公司.NET相关的系统都已经用这个实现统一的登录逻辑了,系统维护的代价小了许多。

说起来其实也是第一次对接微信公众号相关的东西,在走通这条路之前走了不少弯路,不过好在走通了。希望对其他人有帮助。




推荐阅读
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • 本文讨论了如何在codeigniter中识别来自angularjs的请求,并提供了两种方法的代码示例。作者尝试了$this->input->is_ajax_request()和自定义函数is_ajax(),但都没有成功。最后,作者展示了一个ajax请求的示例代码。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • CentOS 6.5安装VMware Tools及共享文件夹显示问题解决方法
    本文介绍了在CentOS 6.5上安装VMware Tools及解决共享文件夹显示问题的方法。包括清空CD/DVD使用的ISO镜像文件、创建挂载目录、改变光驱设备的读写权限等步骤。最后给出了拷贝解压VMware Tools的操作。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 本文介绍了django中视图函数的使用方法,包括如何接收Web请求并返回Web响应,以及如何处理GET请求和POST请求。同时还介绍了urls.py和views.py文件的配置方式。 ... [详细]
  • 延迟注入工具(python)的SQL脚本
    本文介绍了一个延迟注入工具(python)的SQL脚本,包括使用urllib2、time、socket、threading、requests等模块实现延迟注入的方法。该工具可以通过构造特定的URL来进行注入测试,并通过延迟时间来判断注入是否成功。 ... [详细]
  • 网络请求模块选择——axios框架的基本使用和封装
    本文介绍了选择网络请求模块axios的原因,以及axios框架的基本使用和封装方法。包括发送并发请求的演示,全局配置的设置,创建axios实例的方法,拦截器的使用,以及如何封装和请求响应劫持等内容。 ... [详细]
  • 移动端常用单位——rem的使用方法和注意事项
    本文介绍了移动端常用的单位rem的使用方法和注意事项,包括px、%、em、vw、vh等其他常用单位的比较。同时还介绍了如何通过JS获取视口宽度并动态调整rem的值,以适应不同设备的屏幕大小。此外,还提到了rem目前在移动端的主流地位。 ... [详细]
  • 本文介绍了如何使用vue-awesome-swiper组件,包括在main.js中引入和使用swiper和swiperSlide组件,以及设置options和ref属性。同时还介绍了如何在模板中使用swiper和swiperSlide组件,并展示了如何通过循环渲染swipes数组中的数据,并使用picUrl属性显示图片。最后还介绍了如何添加分页器。 ... [详细]
  • uniapp开发H5解决跨域问题的两种代理方法
    本文介绍了uniapp开发H5解决跨域问题的两种代理方法,分别是在manifest.json文件和vue.config.js文件中设置代理。通过设置代理根域名和配置路径别名,可以实现H5页面的跨域访问。同时还介绍了如何开启内网穿透,让外网的人可以访问到本地调试的H5页面。 ... [详细]
  • JavaWeb中读取文件资源的路径问题及解决方法
    在JavaWeb开发中,读取文件资源的路径是一个常见的问题。本文介绍了使用绝对路径和相对路径两种方法来解决这个问题,并给出了相应的代码示例。同时,还讨论了使用绝对路径的优缺点,以及如何正确使用相对路径来读取文件。通过本文的学习,读者可以掌握在JavaWeb中正确找到和读取文件资源的方法。 ... [详细]
author-avatar
我是田小勇2702932553
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有