微信扫码登录的那些二三事…

起因是新年活动主要围绕微信公众号开展,运营同学希望在页面上能给公众号引流蹭点关注,产品同学希望能有一个融合度较高的用户身份识别方式。于是他们一拍即合,做一个微信扫码登录。

微信开放授权的方式

第一种是微信开放平台的网站应用,上线应用需要审核,看了一下审核表感觉好复杂,需要走盖章的流程,临近年关怕审核时间过长,遂放弃...

微信开放平台 (qq.com)

第二种需要认证过的公众号,这个我们是有的,并且开通配置之类的操作也不需要走复杂流程。通过公众号的接口可以获取到用户的 openid, unionid, 昵称, 头像、是否已关注公众号等信息,运营同学直接拍手称好。

微信网页开发 / 网页授权 (qq.com)

最终确定使用微信公众号的 Oauth 开放授权。

登陆流程

后端

后端提供的接口:

  1. /wechat/authorize: 接收 redirectUri, callback 参数,用于构建开放授权的地址。

  2. /wechat/qrcodeTicket: 接收 callback, redirectUri 参数,自行创建一个 event key ,将 event key 和接收到的参数保存至 redis。使用 event key 作为 scene_str 的值,生成带参数的临时二维码 ticket。

  3. /wechat/jwt: 接收 code 参数,使用 code 参数请求微信公众平台获取用户信息,将用户信息入库后颁发 token。

  4. /wechat/isSubscribed: 需要携带有效的 token 请求,通过 user id 查询 open id 再通过公众平台的接口查询用户是否已关注公众号

这里也放一个接口文档供参考

apifox.com/apidoc/shar...

前端

微信内置浏览器

微信内置浏览器比较简单,前端主要有两个页面,分别是活动页面和承载开放平台回调的 callback 页面(使用接收到的 code 请求 /wechat/jwt 接口换取 token)

第一次进入页面时,前端即可将页面重定向至微信公众平台开放授权地址,scope 为 snsapi_base 。snsapi_base 不需要用户授权,公众平台会直接授权并携带 code 查询参数回调页面。页面拿到 code 参数后,使用 code 参数请求后端换取 token 即可。

后端通过 code 可以向公众平台换取用户的 Openid ,通过 Openid 辨别用户。

那,运营想要的关注公众号呢?这都是静默完成的呀...如果直接弹一个公众号的二维码的话太生硬了也不行,用户没有关注的动力...

所以有了 scope snsapi_userinfo,这是一个需要用户手动授权或者通过公众号会话跳转的授权方式,相比于 snsapi_base 可以获取到用户的昵称、头像等信息,并且用户有了经过公众号的行为动向。

运营同学可以在某些页面要求使用 snsapi_userinfo 进行授权,此时前端弹窗显示公众号二维码给用户,提示用户长按扫描二维码以登录。

如果用户不想关注公众号,弹窗也设置了暂不关注的按钮,点击暂不关注会直接跳转 snsapi_userinfo 的授权链接,由微信提示用户授权。

如果用户扫描了二维码关注了公众号,公众号接收到 SCAN 事件或 subscribe 事件,使用对应的 event key 在 redis 中查询,取出 callback 地址,作为开放平台授权地址的 redirect_uri 参数,最终通过公众号被动消息回复的方式发送至微信客户端。

通过公众号会话访问 snsapi_userinfo 授权链接,就可以做到静默授权,不需要用户手动授权。其余操作与 snsapi_base 相同。

非微信内置浏览器

一开始就遇到了重要的问题,非微信内置浏览器不能访问公众平台的开放授权链接。

我的解决方法是使用手机微信进行一个中转,流程大概是:

  • 需要登录,显示二维码(与上面微信内置浏览器的操作一样)
  • 引导用户使用手机扫描二维码,关注公众号进入公众号会话
  • 公众号被动回复授权链接
  • 用户在手机上访问授权链接,网页显示登录成功。

在这个看似复杂的流程中,重要的点在于如何将一个标识从头到尾始终传递。通过这个标识判断桌面端是否登录成功,手机客户端授权给的是哪个用户。

这个标识就是生成二维码时,传入的 scene id。这个 id 会随着 /wechat/qrcodeTicket 一并返回前端,前端通过这个 id 轮询 /wechat/canLogin 接口得知授权状态。

scene id 的传递路径是:用户扫描二维码后,公众号接收到的事件中的 EventKey - 开放平台中的 state 参数 - 回调回应用的 state 参数 - 应用上报 code 参数时再次携带上 state 参数

总结

一个扫码登录做下来感觉门道不少,有一些功能也需要留心。

例如:

  1. 如果登录弹窗的二维码是每隔 60 秒自动刷新一次,而 redis 中将登录信息保存了 5 分钟(考虑有的时候微信推送事件很慢 / 用户弱网加载速度慢之类的情况),那 redis 中就会有多个无用的登录信息。虽然说数据体积也不大,但也可能是一个隐患嘛(我们公司 redis 内存不大),我们后面就改用了用户手动刷新二维码。
  2. 非微信内置浏览器使用的是轮询 /wechat/canLogin 来检测是否可以登录,其实这里也可以换成使用 socket 进行推送。
  3. 那都用 socket 了,不如把二维码过期事件也交由后端推送吧?。。可以监听 redis 的 key 过期删除事件,登录信息被删除之后再使前端的二维码失效
  4. 运营同学想可以动态改变公众号的回复内容而不需要研发介入修改代码,但是消息中又有变量需要进行字符串格式化。我们使用了 mustache.js ,运营同学可以在后台配置模板化的内容

janl/mustache.js: Minimal templating with {{mustaches}} in JavaScript (github.com)

相关推荐
Ulyanov1 分钟前
Python射击游戏开发实战:从系统架构到高级编程技巧
开发语言·前端·python·系统架构·tkinter·gui开发
华仔啊6 分钟前
这 10 个 Vue3 性能优化技巧很实用,但很多项目都没用上
前端·vue.js
手握风云-8 分钟前
JavaEE 进阶第九期:Spring MVC - Web开发的“交通枢纽”(三)
前端·spring·java-ee
天问一16 分钟前
Cesium 处理屏幕空间事件(鼠标点击、移动、滚轮)的示例
前端·javascript
@PHARAOH16 分钟前
WHAT - Vercel react-best-practices 系列(五)
前端·react.js·前端框架
bjzhang7517 分钟前
使用 HTML + JavaScript 实现多会议室甘特视图管理系统
前端·javascript·html
qiqiliuwu18 分钟前
VUE3+TS+ElementUI项目中监测页面滚动scroll事件以及滚动高度不生效问题的解决方案(window.addEventListener)
前端·javascript·elementui·typescript·vue
LawrenceLan19 分钟前
16.Flutter 零基础入门(十六):Widget 基础概念与第一个 Flutter 页面
开发语言·前端·flutter·dart
喔烨鸭22 分钟前
antdv编辑表格,根据选择时间区间展示动态列
前端·vue·表格编辑
天天向上102423 分钟前
el-table 解决一渲染数据页面就卡死
前端·javascript·vue.js