文件名称 | 版本号 | 作者 | 版本 | |
---|---|---|---|---|
OAuth2.0刷新Token | v1.0.0 | 学生宫布 | 8416837 | SpringBoot 2.2.6 SpringCloud Hoxton.SR4 |
需求
- 保证页面不会在正在操作中却弹出Token过期,这样体验不好。除非好久(设定的阈值)没有操作页面了,显示已过期才合理。
假设你已经搭建好OAuth2.0服务器
- 登录(授权)成功后,响应数据长这个样子:
名词 | 释义 | 备注 |
---|---|---|
access_token | 作为消费服务的通行证,逾期会失效 | |
refresh_token | 用来换取新的access_token,会失效,但一般应该设置比access_token晚些失效 | |
expires_in | 多久过期(秒) | 可在数据库设置或者在令牌管理器代码设置 |
- 但如果正在操作中,令牌(通行证)突然到期失效了,那多尴尬
- 因此需要在即将过期或已经过期的时候,刷新Token。——前提是refresh_token没有过期且合法。
解决办法
- 前提:可以成功获取Token,并成功在客户端维护Token、过期时间
1)定时器
- 原理 计算Token过期的时间点,开始计时,当当前时间点接近过期点的时候,执行刷新Token操作。要保证每个页面都可能触发计时器,且只能有一个,当页面刷新时,计时器不受影响。
- 代码 节选
// 从存储获取过期时间
computed: mapGetters(['userInfo', 'isLock', 'isCollapse', 'website', 'expires_in']),
... ...
// 这里是aJax请求拦截器
// 如果即将过期,则调用刷新API,参数是refresh_token, grant_type:授权类型,即refresh_token, scope;并锁住刷新接口
if (this.expires_in <= 1000 && !this.refreshLock) {
this.refreshLock = true
this.$store
.dispatch('RefreshToken')
.catch(() => {
clearInterval(this.refreshTime)
});
this.refreshLock = false
}
// expires_in递减
this.$store.commit("SET_EXPIRES_IN", this.expires_in - 10);
... ...
this.$store.dispatch('RefreshToken')
:调用刷新 API
2)请求拦截式,每次请求权限接口都判断是否可以刷新Token了
- 代码 节选
// 这里是aJax请求拦截器
window.isRefreshing = false
... ...
// futureTime:Token在将来某个时间点过期
if (!window.isRefreshing && futureTime && (futureTime - new Date().getTime() ) <= EXPIRED_IN_THIS_SECONDS ) { // 如果expires_in_time eq 0,则很可能是初次登陆,从而勿须刷新令牌 假如设置还差6秒过期
// 锁 避免多个调用重复刷新
window.isRefreshing = true
// 刷新Token
如果同时调用多个接口,而此时Token已经过期,但refresh_token没有过期的话,最先刷新的接口可以执行成功,另外的接口不会刷新,会导致请求失败。但是之后会正常,因为已经刷新了。见下图: