谈起小程序开发,想必大家都不会陌生了。众所周知,小程序开发的官方文档是要求开发者需要自行维护登录态的。那么小程序服务端开发的登录态维护大家是如何做的呢?本文适用微信和QQ小程序的自定义登录态维护。
官方登录态原理说明
以微信为例,我们先来看看官方对小程序登录态流程的处理说明。小程序可以通过微信官方提供的登陆能力方便地获取微信提供的用户身份标志,快速建立小程序内的用户体系。
登录流程时序
官方的说明已经很详细了,如下:
1.调用 wx.login() 获取临时登录凭证code,并将code回传到开发者服务器(服务端代码);
2.服务端代码开发者调用auth.code2Session接口,换取用户唯一标识OpenID和会话密钥session_key;
3.之后开发者服务器可以根据用户标志来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。
到这里有些同学要发言了,看官方的登录态维护多完美,直接用好像就行了,这里第三点为什么说还要生成自定义登录态。嗯,我们来看下一则注意事项:
session_key不可以暴露
意思就是,session_key是要自行保管的不能对外暴露。虽然这里官方未详尽说明原因,但小马这里大概可以告诉大家,openid和session_key这两块用户数据其实相当于小程序用户的登录态数据,也就是登录态token,如果被劫取,就意味着对方拥有了合法的登录态token,对方就可以使用这两个数据任意调起小程序其他的功能接口,伪造用户行为。
如何实现自定义登录态维护
那我们该如何完美地实现自定义登录态维护呢?小马来提供一个已生产实践的参考方案。我们还是参照上面的流程时序图理清一下自定义登录态维护思路。
1.前端调用 wx.login() 获取临时登录凭证code ,并将code回传到开发者服务器(服务端代码);
2.服务端代码使用code调用服务端 auth.code2Session接口,换取用户唯一OpenID和会话密钥session_key;
3.自定义登录态,我们这里考虑与openid和session_key关联,所以可以使用加盐和openid和session_key等方式哈希作为key,然后session_key作为value,存储于Redis,设置过期时间假设为2天。到这里,我们的自定义登录态存储完毕,我们下面称其为token。我们同时需要将openid和token返回给前端;
4.前端得到token和openid并将其存储在本地存储,每次发起请求先判断是否本地存储有token,如果有则直接再请求参数中带上openid 和token传值到服务端校验;如果没有token则需要执行流程1登录;
5.但开发者服务端获取到前端传值token和openid,为减轻压力,先作参数合法校验,然后哈希openid得到token与接收到的token校验,如果不等,说明参数被篡改,如果相等,则获取Redis中key为token的值,如果可以取到,说明自定义登录态有效。
好了,维护自定义登录态的思路就是这样了。然而,细心的同学可能发现了什么问题。就这样结束了吗?当然不是了。
问题优化
比如我们来思考一个问题,如果在我们Redis中的token并未过期,而此时由于前端的登录态数据被窃取,根据我们之前的分析,测试窃取者会拥有最多2天的有效登录态。于是为了使其失效,前端决定重新登录获取新的登录态。然后,此时我们发现,我们无从让旧的token失效,以为我们不知道上一个token哈希时的session_key。如何解决呢?我们将openid和每次最新登陆获取的session_key的关联关系记录在DB或者Redis,每次校验时将Redis取到的value和这里存储的每次最新登陆获取的session_key作比较,如果不等,则说明是已经被失效的登录态token。
我们再来思考一个问题,如果使用伪造的code不断刷新auth.code2Session接口会如何?此时,我们第一想到的就是恶意攻击爆破。我们分析如果不断请求的话压力将在auth.code2Session接口侧,微信接口服务会做请求频率相关限制。那么我们自己是否也应该作一下呢?答案自然是肯定的,一个错误的code直接缓存错误结果2小时,不断使用同一code错误码的恶意请求将被缓存直接拦截。细心的同学要问题了,那么恶意伪造token刷接口呢?这个问题其实前面有作了哈希校验,而且如果再加上请求频率限制应该会好点。如果你还是不放心,当然也可以像code码一样再做一层防刷机制,防止缓存穿透。
再来一个问题,因为我们自定义了登录态,自然也就基于自定义的token进行登录态交互。上面说到我们如果需要调用小程序的其他接口,必须有openid和session_key。我们知道我们会把最新的session_key存储在本地可以直接取,那么问题是假如微信服务器session_key已经过期,但是你的本地Redis还没过期呢,就会出现调用接口的时候登录态生效不是?嘿嘿,这点小马也考虑到了。官方明确指出给予的session_key对开发者是透明的,也不会告知过期时间,只有前端前端使用接口 wx.checkSession可以校验session_key是否有效。于是,前端每次登录不仅仅是token还要先判断一下微信服务器session_key是否过期就可以避免上述问题了。
官方文档说明
关于微信和QQ小程序的自定义登录态维护方案就介绍到此了。欢迎指正交流。或者您有更好的方案也可以一起分享交流哈。
参考文档:
小程序登录官方文档
会话密钥session_key有效性