微信小程序登录流程详解

微信小程序是一个基于微信开发出来的“即时APP”,无需下载安装,仅需要在微信中用户扫一扫或者搜一下即可开打应用,完全符合小程序诞生时的“即用即走”,因此它在很短的时间内得到了飞速的发展。

微信官方业务流程

小程序可以通过微信官方提供的登录能力方便的获取微信提供的用户身份标识。快速建立小程序用户体系。

一、我们先来看看微信登录的整个过程

在小程序官网里面会提到一个小程序的登录逻辑,这是官方推荐的登录逻辑,也就是所谓的小程序登录态维护逻辑,这里是官方的图

1、用户打开微信小程序。

    • 也可以是在需要的时候处理这个逻辑,比如退出重新登录等,不一定是打开小程序的时候执行这个逻辑,具体看业务需要。
    • 冷启动:小程序首次打开或销毁后再次被打开,此时小程序需要重新加载启动,即冷启动。会检查小程序是否有最新版本,如果有则将异步下载最新版本,但是仍将运行当前版本等到下一次冷启动时再运行最新版本。
    • 热启动:如果用户已经打开过某小程序,然后在一定时间内再次打开该小程序,此时小程序并未被销毁,只是从后台状态进入前台状态,这个过程就是热启动。发布新版本之后,无法立刻影响到所有现网用户,但最差情况下,也在发布之后 24 小时之内下发新版本信息到用户。用户下次打开时会先更新最新版本再打开。

2、获取微信登录凭证code

微信小程序通过wx.login()获取微信登录凭证code。

    • code是一个带有时效性的凭证,有效时间是5分钟,它就相当于一个会过期的临时身份证,过期之后需要调用wx.login()重新生成登录凭证。
    • 假如没有这个登录凭证,而是直接通过wx.login()直接拿到微信用户的编号1,再通过wx.request请求接口拉取到微信用户1在我们业务侧的个人信息,那么黑客就可以通过遍历所有的id,把整个业务侧的个人信息全部拉走,甚至相关的其他数据。
    • wx.login()是限频接口,一天的调用总次数不多于该小程序pv(访问量)的两倍。“限频接口”指的是一个用户在一段时间内不允许频繁调用的 wx 接口,此类接口一般会调用到微信后台系统资源,为了保护系统,同时防止用户资源被滥用,开发者需要对此类接口做适度的频率限制,不能无节制地调用。
wx.login({ 
    success (res) { 
        if (res.code) { 
            //发起网络请求 
            wx.request({ 
                url: 'https://example.com/onLogin', 
                data: { code: res.code } 
            }) 
        } else {
         console.log('登录失败!' + res.errMsg) 
        } 
    } 
})

3、发送code到开发者服务器,到微信服务器换取微信用户身份id

将微信登录凭证code 发送给开发者服务器,也就是我们自己的开发服务器。开发者服务器接收到code 后,通过code2Session这个api接口来获取真正需要的微信用户的登录态session_key和 openid 和 unionid。

https://qyapi.weixin.qq.com/cgi-bin/miniprogram/jscode2session?access_token=ACCESS_TOKEN&js_code=CODE&grant_type=authorization_code

    • access_token 是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
    • 准确来说session_key才是真正的微信登录态信息,但是把 openid 和 unionid加起来一起理解,也可以笼统地理解为这些都是微信的登录态信息。
    • AppID和AppSecret,在小程序管理平台-设置-开发设置,他们是微信鉴别开发者身份的重要信息。AppID是公开信息,AppSecret是开发者的隐私数据,生成之后需要开发者保存起来,如果发现泄露需要在小程序管理平台重置AppSecret。
    • openid是微信用户的唯一标识;
    • session_key是微信服务器给开发者服务器颁发的身份凭证,官方说需要定期使用wx.checkSession检测,但是在实际的场景里面其实也可以不用,用和不用主要看业务需求;早起官方是返回 expire_time 过期时间的,但是后面取消了,这里有一个比较新的官方回复:用户越频繁使用小程序,session_key有效期越长,…… | 微信开放社区,有效期是3天,但是这个不一定是固定的,具体看业务需求,总的原则就是维护一个自定义登录态,自定义登录需要和微信登录态关联。
    • unionid是用户在微信开放平台的唯一标识符,如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过unionid来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的unionid是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionId是相同的。

4、生成业务登录凭证SessionId,绑定微信用户身份id和业务用户身份

然后需要开发者服务器自己生成一个自定义的登录态(例如业务 token,官方说的3rd_session)来保存从微信服务器返回来的微信登录态相关信息(session_key和 openid 和 unionid),并做关联处理,然后返回给小程序客户端。有了这个绑定信息,小程序在下次需要用户登录的时候就可以不需要输入账号密码,因为通过wx.login()获取到code之后,可以拿到用户的微信身份openid,通过绑定信息就可以查出业务侧的用户身份id,这样静默授权的登录方式显得非常便捷。

    • 关联处理就是自定义登录态和微信的登录态相关联,这样的话就不需要维护多个登录态,只需要维护一个就可以了。
    • 关联处理之后需要将这个自定义登录态信息保存起来,可以放到数据库或者本地文件或者 例如 redis 之类的缓存服务里面,以便方便后续使用,而不需要每次都请求微信服务器(微信服务器对这个请求的频率是有限制的)。
    • 注意这里不返回微信登录态相关信息,只返回自定义的登录态信息。
    • 自定义登录态的信息不仅可以包含 token,也可以包含一些用户权限信息,或者其他信息,因为是自定义的登录态,维护也是很自定义的。
    • 一般自定义的登录态的超时时长需要比微信的登录态要长。

5、缓存用户登录态SessionId

小程序客户端接收到返回的自定义登录态信息,从而判断用户是否登录成功,登录成功的话,就将自定义登录态信息保存到本地的存储。

    • 本地的存储可以是微信小程序提供的 app.globaldata,也可以是 localstorage,注意,小程序不支持 cookie。
    • 保存到本地存储的好处就是,后续使用的这个自定义登录态就不需要再次跟服务器进行交互来获取了,只需要调用本地存储就行了,这里是为了优化性能和避免浪费资源。

6、携带SessionId访问业务服务器

小程序客户端通过wx.request带上自定义登录态(token)进行对开发者服务器(业务接口服务器)访问。

header: { 
    'xxxxtoken': token, 
}

7、验证并返回业务数据

开发者服务器的业务接口接收到请求,并且请求里面携带了自定义的登录态,通过校验之后,会返回相关信息。这里的验证不是说验证session_key是否在有效期内,而是验证是否存在session_key和openid,微信官方未提供服务端验证方法,也不会把 session_key 的有效期告知开发者。

    • 校验是将小程序客户端携带过来的自定义登录态和开发者服务器缓存起来的自定义登录态进行对比,会去确认是否和用户的 openid或者 unionid 和 session_key 相匹配。
    • 如果匹配,就可以马上返回相关信息。
    • 如果不匹配,就可以马上返回相关信息,告知小程序客户端无法访问业务接口。
    • 如果匹配结果是自定义登录态超时了,就可以马上返回相关信息,告知小程序客户端需要重新运行登录逻辑
    • 如果是匹配结果是自定义登录态没有超时,但是微信登录态超时了,那么就会开发者服务器就会再次发起code2Session进行微信登录态更新。

开发者如果遇到因为 session_key 不正确而校验签名失败或解密失败,请关注下面几个与 session_key 有关的注意事项。

  1. wx.login 调用时,用户的 session_key 可能会被更新而致使旧 session_key 失效(刷新机制存在最短周期,如果同一个用户短时间内多次调用 wx.login,并非每次调用都导致 session_key 刷新)。开发者应该在明确需要重新登录时才调用 wx.login,及时通过 auth.code2Session 接口更新服务器存储的 session_key。
  2. 微信不会把 session_key 的有效期告知开发者。我们会根据用户使用小程序的行为对 session_key 进行续期。用户越频繁使用小程序,session_key 有效期越长。
  3. 开发者在 session_key 失效时,可以通过重新执行登录流程获取有效的 session_key。使用接口 wx.checkSession可以校验 session_key 是否有效,从而避免小程序反复执行登录流程。
  4. 当开发者在实现自定义登录态时,可以考虑以 session_key 有效期作为自身登录态有效期,也可以实现自定义的时效性策略。

二、通过自定义登录态来管理小程序登录

按照官方推荐的方式是使用自定义登录态来管理整个微信小程序登录流程的,虽然说是通过一个自定义登录态来管理,但是其实里面是有变通的。

这里有2种方式来做:

方式一:小程序打开的时候先检查小程序本地是否有存储的自定义登录态,

    • 如果没有,则代表是首次登录,要自动执行完整的登录流程,
    • 如果有,则需要判断这个自定义登录态是否过期,这里判断的方式有2种,一种是开发者服务器提供一个接口来检查,一种是在这个自定义登录态数据里面加上过期时间,判断是否过期。
      • 过期,则自动发起完整的登录流程。
      • 不过期,就继续使用本地保存的自定义登录态。

方式二:小程序打开的时候先发起wx.checkSession检查微信登录态是否过期:

  1. 如果过期,则发起完整的登录流程。
  2. 如果不过期,则继续使用本地保存的自定义登录态。(如果本地的自定义登录态没有的话,那么也是要强制发起完整的登录流程的)

上面说的方式都是打开小程序的时候做的,但是也要考虑到一种情况,就是自定义登录态在用户使用小程序的过程中过期了,那么这时候也是需要强制执行完整的登录流程的。

相对来说方式二比较好,方式二的好处是不需要小程序服务端来参与校验,而是在小程序端调用API。这种方式实际上是将维护登录态的机制委托给了微信维护的session_key,开发者不需要在自定义的登录态中保存有效期信息。腾讯云开发的wafer项目便采取了这种方法。

但是,请看四

三、HTTP 请求跟踪状态

上面提到的方式二是在打开小程序的时候做的,这里是在每次发起 HTTP 请求的时候做的,这个是不一样的逻辑。

当小程序客户端访问接口的时候,开发者服务器返回说你的自定义登录态过期了,你就需要重新发起完整的登录,但这里有2个点是需要注意的:

  1. 由于 HTTP 是无状态的,所以不能跟踪状态,发起 HTTP 请求然后服务器返回自定义登录态过期的结果,然后就完结了,没办法暂存起原来的 HTTP 请求,以便当我们有可用的自定义登录态的时候,继续使用。所以我们需要在发起 HTTP 请求的时候做判断,看是否需要发起完整的登录流程。
  2. 在小程序里面,经常会有访问开发者服务器的情况,但是如果每个接口都需要这样直接判断的话,就会产生很多冗余代码,而且没办法维护,万一登录流程改变了,就要全部代码改一遍了。

所以这里就需要进行封装一下,可以按自己需要实现。这是一个宏观的,每个请求都要检查一下登录态的逻辑图:

每个请求都会检查一次登录态,不管是自定义登录态还是微信登录态,反正只要发起跟开发者服务器交互的 HTTP 请求的之前都会检查。

细分逻辑的话就会变成这样:

这里的逻辑是微信登录态和自定义登录态都会检查一遍,任何一个登录态过期都会发起完整的登录逻辑,其实我觉得这里也可以只判断自定义登录态的状态也可以的。

不过由此也带来一个问题,就是如果一个页面里面有多个请求同时发起的话,那么就会出现同时检查到登录态过期,然后同时发起登录的情况,这个问题可能会导致开发者服务器的校验登录数据出现问题,有网友这里用到 promise.race 来解决。

四、wx.checkSession罢工

我们通过实验发现,在 session_key 已过期的情况下,wx.checkSession 有一定的几率返回true。checkSession用来判断登录信息既session_key是否已经在微信端失效,即自定义登录态失效了过期了但是session_key在微信端没有失效,checkSession也是会返回seccess的。即增加wx.checkSession步骤并不能百分百保证登录态不会过期,后续仍然需要对不同的状态码进行处理。

社区也有相关的反馈未得到解决:

  • 小程序解密手机号,隔一小段时间后,checksession:ok,但是解密失败
  • wx.checkSession 有效,但是解密数据失败
  • checkSession 判断 session_key 未失效,但是解密手机号失败

所以结论是:wx.checkSession可靠性是不达 100% 的。

基于以上,我们需要对 session_key 的过期做一些容错处理:

1、发起需要使用 

session_key 的请求前,做一次 wx.checkSession 操作,如果失败了刷新登录态。

2、后端使用

session_key解密开放数据失败之后,返回特定错误码(如:USER_WX_SESSIONKEY_EXPIRE),前端刷新登录态。

猜你喜欢

转载自blog.csdn.net/ly2983068126/article/details/79585149