refresh token是token之后的一个进一步的发展。它主要是为了让用户无感知的延长登录状态的有效期。
于是现在的流程(省略掉了一些细节)是:
账号密码登录。
服务器返回access_token和refresh_token两个字符串,都要存到本地而且永久存储。
access_token在服务器的生命是1天,refresh_token在服务器的生命是7天。这个天数可以自己公司自己定。将生命值一起存到本地缓存,比如
{
access_token:{
value: 'xxx',
expires: '一天以后的时间戳'
},
access_token:{
value: 'ooo',
expires: '7天以后的时间戳'
}`
}
发请求之前要做一些本地的判断,要判断access_token是否过期,没过期的话,请求要header携带access_token,服务器判断access_token是否过期,过期的话,返回401错误。
你的axios要拦截401错误,当拦截到之后,立即向一个特设的api发请求,此时header携带的是refresh_token。
服务器判断refresh_token是不是过期,如果没有过期,那么返回新的access_token和新的refresh token,然后把挂起的请求重新发出去。这里注意,虽然refresh token的有效期是access_token的好几倍,但是它往往撑不到生命结束,因为access_token一天就过期了,那么refresh token即便没有过期,也会被刷新成新的refresh token。
之前说了,有些细节我省略掉了,全部流程可以看这个图。这个图的争议部分就是access_token的“是否过期”和“是否即将过期”,你可以判断两次,也可以只判断是否过期,都成。
另外注意:
axios的拦截器怎么写,其实还是很简单的,因为拦截器本质就是挂起请求,所以中间你插一个请求新的access_token的过程是没问题的。
图里没有提到refresh_token本地过期的判断,应该考虑上,如果本地过期,就立即跳到重新登录。
有同学说,那我一打开项目就已经refresh token过期了,怎么判断?
废话么这不是,项目加载过程中就会做ajax请求啊,请求如果过期了就跳转到登录啊。就是上面的流程。有同学说,我不用refresh_token行不行?
当然行,refresh_token就是为了“无感知的延长用户登录有效期”,不用的话,更简单,access_token一天之后一定过期,一定要重登陆。有同学说,不用refresh token,只是服务器在access_token临近过期的时候延长access_token的服务器有效期行不行?
也行!但是这里面就有个问题,就是服务器要判断你的access_token是不是临近过期,这就需要任何一个请求发到服务器都要判断一次token的有效期,是不是非常浪费服务器性能?相对而言,浏览器向特定接口发一次请求,是不是非常节省服务器性能?有同学说,还是不想用refresh token,只在本地判断access_token是不是临近过期行不行?只要本地临近过期,就向服务器发送请求,请求access_token延期,行不行?
行,但是安全性低。这就涉及到refresh_token的另一个作用,提升安全性。因为access_token你每天可能会发200次,但是refresh_token你每天发一次(上面说了,虽然refresh_token有7天生命,但往往1天就随着access_token刷新了),你说哪个安全?有同学说,如果我正在向服务器发送多个请求,比如同时发了10个请求,那么我们知道,请求服务器特定接口,是需要时间的,比如用时1秒,这1秒内10个请求都获得了401错误,axios会向特定接口发10个请求,这时候服务器会返回10个不同的access_token吗?
这就需要服务器做处理了,也就是后端同学需要考虑的事情,他要保证服务器发回来的10个token的值必须是一致的。也就是说,服务器新生成的access_token和refresh token在短时间内(比如3秒内)是不允许改变的。有同学说,拿着access_token和refresh_token是不是能永久登录了?如果被外人拿到怎么办?
这就需要服务器判断用户异常行为,然后警报。这跟cookie/session方案的安全性是一样的,没有警报的话,谁知道是不是你本人呢?阿里腾讯也不敢说异地登录的就一定是你本人,或者一定不是你本人吧?
再有,你用账号密码登录的话,是一定会刷新access_token和refresh_token的,那么别人拿的access_token和refresh_token也就立即失效了。有同学说,怎么注销access_token呢?
全文说的access_token和refresh_token,并不是JWT方案,而是传统token方案,JWT方案才有“注销困难”(但也是有办法的)的问题,因为JWT在服务器只存在计算式的校验,而不是跟已有数据比对式的校验,JWT是计算验证,不是比对验证。这里不提JWT方案,只说传统token方案的话,因为token是在服务器是有储存的,发到服务器的token是要进行比对的。所以注销很容易啊,把服务器存的那份删掉就行了。