微信小程序是一个基于微信开发出来的“即时APP”,无需下载安装,仅需要在微信中用户扫一扫或者搜一下即可开打应用,完全符合小程序诞生时的“即用即走”,因此它在很短的时间内得到了飞速的发展。
小程序可以通过微信官方提供的登录能力方便的获取微信提供的用户身份标识。快速建立小程序用户体系。
在小程序官网里面会提到一个小程序的登录逻辑,这是官方推荐的登录逻辑,也就是所谓的小程序登录态维护逻辑,这里是官方的图


微信小程序通过wx.login()获取微信登录凭证code。
wx.login({ success (res) { if (res.code) { //发起网络请求 wx.request({ url: 'https://example.com/onLogin', data: { code: res.code } }) } else {console.log('登录失败!' + res.errMsg) } }
})
将微信登录凭证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
然后需要开发者服务器自己生成一个自定义的登录态(例如业务 token,官方说的3rd_session)来保存从微信服务器返回来的微信登录态相关信息(session_key和 openid 和 unionid),并做关联处理,然后返回给小程序客户端。有了这个绑定信息,小程序在下次需要用户登录的时候就可以不需要输入账号密码,因为通过wx.login()获取到code之后,可以拿到用户的微信身份openid,通过绑定信息就可以查出业务侧的用户身份id,这样静默授权的登录方式显得非常便捷。
小程序客户端接收到返回的自定义登录态信息,从而判断用户是否登录成功,登录成功的话,就将自定义登录态信息保存到本地的存储。
小程序客户端通过wx.request带上自定义登录态(token)进行对开发者服务器(业务接口服务器)访问。
header: { 'xxxxtoken': token,
}
开发者服务器的业务接口接收到请求,并且请求里面携带了自定义的登录态,通过校验之后,会返回相关信息。这里的验证不是说验证session_key是否在有效期内,而是验证是否存在session_key和openid,微信官方未提供服务端验证方法,也不会把 session_key 的有效期告知开发者。
开发者如果遇到因为 session_key 不正确而校验签名失败或解密失败,请关注下面几个与 session_key 有关的注意事项。
- wx.login 调用时,用户的 session_key 可能会被更新而致使旧 session_key 失效(刷新机制存在最短周期,如果同一个用户短时间内多次调用 wx.login,并非每次调用都导致 session_key 刷新)。开发者应该在明确需要重新登录时才调用 wx.login,及时通过 auth.code2Session 接口更新服务器存储的 session_key。
- 微信不会把 session_key 的有效期告知开发者。我们会根据用户使用小程序的行为对 session_key 进行续期。用户越频繁使用小程序,session_key 有效期越长。
- 开发者在 session_key 失效时,可以通过重新执行登录流程获取有效的 session_key。使用接口 wx.checkSession可以校验 session_key 是否有效,从而避免小程序反复执行登录流程。
- 当开发者在实现自定义登录态时,可以考虑以 session_key 有效期作为自身登录态有效期,也可以实现自定义的时效性策略。
按照官方推荐的方式是使用自定义登录态来管理整个微信小程序登录流程的,虽然说是通过一个自定义登录态来管理,但是其实里面是有变通的。
这里有2种方式来做:
方式一:小程序打开的时候先检查小程序本地是否有存储的自定义登录态,

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

上面说的方式都是打开小程序的时候做的,但是也要考虑到一种情况,就是自定义登录态在用户使用小程序的过程中过期了,那么这时候也是需要强制执行完整的登录流程的。
相对来说方式二比较好,方式二的好处是不需要小程序服务端来参与校验,而是在小程序端调用API。这种方式实际上是将维护登录态的机制委托给了微信维护的session_key,开发者不需要在自定义的登录态中保存有效期信息。腾讯云开发的wafer项目便采取了这种方法。
但是,请看四
上面提到的方式二是在打开小程序的时候做的,这里是在每次发起 HTTP 请求的时候做的,这个是不一样的逻辑。
当小程序客户端访问接口的时候,开发者服务器返回说你的自定义登录态过期了,你就需要重新发起完整的登录,但这里有2个点是需要注意的:
所以这里就需要进行封装一下,可以按自己需要实现。这是一个宏观的,每个请求都要检查一下登录态的逻辑图:

每个请求都会检查一次登录态,不管是自定义登录态还是微信登录态,反正只要发起跟开发者服务器交互的 HTTP 请求的之前都会检查。
细分逻辑的话就会变成这样:

这里的逻辑是微信登录态和自定义登录态都会检查一遍,任何一个登录态过期都会发起完整的登录逻辑,其实我觉得这里也可以只判断自定义登录态的状态也可以的。
不过由此也带来一个问题,就是如果一个页面里面有多个请求同时发起的话,那么就会出现同时检查到登录态过期,然后同时发起登录的情况,这个问题可能会导致开发者服务器的校验登录数据出现问题,有网友这里用到 promise.race 来解决。
我们通过实验发现,在 session_key 已过期的情况下,wx.checkSession 有一定的几率返回true。checkSession用来判断登录信息既session_key是否已经在微信端失效,即自定义登录态失效了过期了但是session_key在微信端没有失效,checkSession也是会返回seccess的。即增加wx.checkSession步骤并不能百分百保证登录态不会过期,后续仍然需要对不同的状态码进行处理。
社区也有相关的反馈未得到解决:
所以结论是:wx.checkSession可靠性是不达 100% 的。
基于以上,我们需要对 session_key 的过期做一些容错处理:
1、发起需要使用
session_key 的请求前,做一次 wx.checkSession 操作,如果失败了刷新登录态。
2、后端使用
session_key解密开放数据失败之后,返回特定错误码(如:USER_WX_SESSIONKEY_EXPIRE),前端刷新登录态。