小程序静默登录与维护自定义登录态

马丁路德
• 阅读 2292

1.背景

在小程序中,openid是一个用户对于一个小程序/公众号的标识,开发者可以通过这个标识识别出用户,就如同你的身份证一样。

2.什么是静默登录?

在普通的应用中,用户通过表单验证登录建立用户体系,这种常见的登录方式一般是通过登录页面表单进行登录,对用户来说是有感的。

在小程序中,由于是基于微信,可以通过微信官方提供的API能力,使我们能够无感知得获取用户身份标识(openid),快速建立小程序内的用户体系,对用户来说是无感知的,因此是由程序来完成这个自动的登陆过程。

2.1登录流程时序

下图取自微信官方

小程序静默登录与维护自定义登录态

// 小程序端调用wx.login(),获取code并且上传到服务器
export async function doLogin() {
    if (isLogin) return false
    isLogin = true
    removeCache('token')
    const { code } = await wxp.login()
    const data = await login({ code })
    setCache('token', data.data.token)
    isLogin = false
    return true
}

// 服务端拿到code,调用auth.code2Session接口换取openid
const getOpenid = async function (appid, secret, code) {
    const resData = await axios.get('https://api.weixin.qq.com/sns/jscode2session?appid=' + appid + '&secret=' + secret + '&js_code=' + code + '&grant_type=authorization_code');
    return resData.data;
} 

总结流程:

  • 小程序端调用wx.login(),获取code并且上传到服务器
  • 服务器根据code,并且调用微信auth.code2Session接口换取openid
  • 后台服务器根据openid生成自定义token返回前端并且存储起来,后续业务逻辑用token来识别用户身份

3.如何维护自定义登录态

让我们来看下官方的处理方式:

小程序静默登录与维护自定义登录态

wx.checkSession({
  success () {
    //session_key 未过期,并且在本生命周期一直有效
  },
  fail () {
    // session_key 已经失效,需要重新执行登录流程
    wx.login() //重新登录
  }
}) 

由图中我们可以知道,真正决定登录态的是微信的checkSession接口。因此每次检查用户登录态是否有效就先调用一个checkSession接口,如果session_key失效,再发起登录流程。

4.静默登录整体流程

4.1app.onLaunch中发起登录

由于大部分的接口调用都需要token验证,因此在小程序启动的周期函数app.onLaunch中发起静默登录最为合适不过了。

小程序静默登录与维护自定义登录态

4.2处理小程序不支持异步阻塞

由于小程序的启动流程中,页面级和组件级的生命周期函数都不支持异步阻塞;因此会造成一个情况,app.onLaunch中发起的wx.login还没有成功的时候,页面级的生命周期函数已经向服务器发起请求。由于我们的接口设计大部分都是需要验证的,此时登录还未成功,token也还没有正确返回,因此页面级的生命周期发起的数据获取接口肯定是会报错的(例如返回了401)

4.2.1粗糙的方案

采用回调函数的方式

//app.js
this.globalData.wxp.showLoading({
        title: '登录中...'
      });
      await login();
      this.globalData.hasLogin = true;
      if (this.checkLoginReadyCallback) {
        this.checkLoginReadyCallback();
      }
      this.globalData.wxp.hideLoading();

页面的生命周期中
async onLoad() {
    if (app.globalData.hasLogin) {
    //如果已经登录了直接获取数据
      this.getUserInfo();
      this.getEvent();
    } else {
    //未登录定义下回调函数,等app.js登录成功之后进行调用
      app.checkLoginReadyCallback = async () => {
        this.getUserInfo();
        this.getEvent();
      };
    }
  }, 
  • 优点:简单粗暴
  • 缺点:代码结构差;如果是多个页面为启动页,则需要多个页面都定义回调函数(假设使用了小程序onShare模式)

4.2.2优雅的方式

借助fly.js库,实现对请求进行上锁机制。流程:app.js中发起登录,同时页面中也会发起请求。在请求拦截器中判断请求的接口是否为白名单(不需要token验证的接口)接口和token是否存在;如果都为false,锁住当前请求进入请求队列,执行登录流程。等待登录流程成功之后解锁请求队列,继续发起页面级的请求任务。如下为请求拦截器中的代码:

//拦截处理
fly.interceptors.request.use(async (request) => {
    //没有token且请求不是白名单的都锁住
    if (
        !getCache('token') &&
        !whiteList.some((item) => request.url.startsWith(item))
    ) {
        fly.lock()
        //去登陆 成功之后再unlock
        await doLogin()
        fly.unlock() //解锁后,会继续发起请求队列中的任务
    }

    if (getCache('token') && !fly.config.headers['Authorization']) {
        request.headers['Authorization'] = getCache('token')
    }
    request.headers['Content-Type'] = 'application/x-www-form-urlencoded'

    return request
}) 

当然,自定义登录态也会存在过期的情况,我们可以在响应拦截器中捕获出错进行处理:当检测到401token过期代码时,需要把请求队列后面的请求都锁死,防止多次出现401自定义登录态过期的情况,然后发起登录,登陆成功之后再进行解锁行为,触发后续的请求队列执行,并且重新执行本次由于token过期被服务器拒绝的接口,否则会造成请求失败的情况(由于静默登录是用户无感知的,突然出现身份验证信息过期会使用户感觉到特别地奇怪,因此需要重新执行本次请求操作而不是由用户再次点击或者其他的行为再发起):

// 响应拦截
fly.interceptors.response.use(
    (response) => {
        //只将请求结果的data字段返回
        return response.data
    },
    async (err) => {
        if (err.status === 401) {
            //401之后,把后面的请求都锁死 防止再次401
            fly.lock()
            removeCache('token')
            //去登陆 成功之后再unlock
            const isLoginSuccess = await doLogin()
            if (isLoginSuccess) {
                fly.unlock()
            }
                        //新执行本次由于token过期被服务器拒绝的接口
            return fly.request(err.request)
        }
    }
) 

由于请求有可能是并发的,为了防止登录被多次执行,因此对doLogin函数进行了小小的改造(尽管写得很不优雅,但是能力有限,大佬们赐教了):

export async function doLogin() {
        //如果正在登录中则不执行
    if (isLogin) return false
    isLogin = true
        //修改状态为登录中,反正重复多次登录
    removeCache('token')
    const { code } = await wxp.login()
    const data = await login({ code })
    setCache('token', data.data.token)
    isLogin = false
    return true
} 

4.3 整体流程图

小程序静默登录与维护自定义登录态

5.写在最后

细节的读者即可发现,api请求中并未设置最大请求数量的(微信小程序最大支持五个api同时发起),这点是需要补充进来的。总体写下来作者觉得在实现方式上还有进步的空间,作者能力有限,也是一边学习一边探讨,如果哪里写得有问题,请给我留言指出,感谢!

点赞
收藏
评论区
推荐文章
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
CuterCorley CuterCorley
3年前
uni-app入门教程(7)第三方登录和分享
前言本文主要介绍了APP开发的两大基本功能,即第三方登录登录和分享:包括登录通用配置,微信小程序和APP的第三方登录方式,和分享到聊天和朋友圈,使用uniapp实现有不同的接口和实现方式。一、通用配置因为小程序和APP登录接口不同,需要在前端进行跨端兼容处理,同时微信等平台的小程序一般只支持所属宿主程序的第三方登录,而无法包括其他的
Jacquelyn38 Jacquelyn38
3年前
手写一个仿微信登录的nodejs程序
前言首先,我们看一下微信开放文档中的一张图:上面的一幅图中清楚地介绍了微信登录整个过程,下面对图上所示进行总结:一、二维码的获得1.用户打开登录网页后,登录网页后台根据微信OAuth2.0协议向微信开发平台请求授权登录,并传递事先在微信开发平台中审核通过的AppID和AppSecrect等参数;2.微信开发平台对AppID等参数进行验证,并向
梦
3年前
微信小程序new Date()转换时间异常问题
微信小程序苹果手机页面上显示时间异常,安卓机正常问题image(https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/b691e1230e2f15efbd81fe11ef734d4f.png)错误代码vardate'2021030617:00:00'vardateT
关于皕杰小程序打开公众号推文
关联公众号1.首先,登录微信公众号的后台,然后找到小程序,并点击小程序管理;其次,在小程序管理中,找到快速注册并认证小程序,点击以后用管理员身份验证一下。然后搜索想要关联的小程序,点击下一步,就关联成功了。2.小程序与公众号关联的时候,有
Easter79 Easter79
3年前
Taro小程序自定义顶部导航栏
微信自带的顶部导航栏是无法支持自定义icon和增加元素的,在开发小程序的时候自带的根本满足不了需求,分享一个封装好的组件,支持自定义icon、扩展dom,适配安卓、ios、h5,全面屏。我用的是京东的Taro多端编译框架写的小程序,原生的也可以适用,用到的微信/taro的api做调整就行,实现效果如下。!在这里插入图片描述(https://i
Wesley13 Wesley13
3年前
.NET之微信小程序获取用户UnionID
前言:  在实际项目开发中我们经常会遇到账号统一的问题,如何在不同端或者是不同的登录方式下保证同一个会员或者用户账号唯一(便于用户信息的管理)。这段时间就有一个这样的需求,之前有个客户做了一个微信小程序商城(店主端的),然后现在又要做一个会员购物端的小程序商场。首先之前用户登录凭证都是使用微信openid来做的唯一标识,而现在客户需求是要做到用户
艾木酱 艾木酱
2年前
应用实战|微信小程序开发示例之Super课表
此示例提供了使用MemFireCloud构建一个课表的小程序的步骤。小程序用到的MemFireCloud的功能包括:云数据库:存储小程序数据表的信息。用户验证:小程序使用MemFireCloud提供的用户认证的API接口,快速完成用户注册登录操作。云存储:存储小程序的注册用户上传的头像。行级安全策略:采用RLS策略来限制用户访问行为,用户可
曼成 曼成
8个月前
一文教你如何在小程序中快速接入验证码短信API
在微信小程序中接入验证码短信API,可以为用户提供便捷的验证服务。本文将详细介绍如何在小程序中实现这一功能,包括UI设计、API请求以及代码实现。