背景

Token 通常用于用户认证和权限管理,当 Token 过期时一般会重定向到登录页,用户的操作会受到影响,因此需要无感刷新。以下用双token实现。

原理

每次发起请求时,检查访问令牌的有效性。
如果访问令牌过期,暂停当前请求并使用刷新令牌获取新的访问令牌。
使用新的访问令牌重新发起之前被暂停的请求。
更新应用中的 Token 信息。

以上原理是其他博客里看到的描述,但是在他们的代码里,都没有暂停请求,只是存储了失败的请求。

vue+axios场景下实现无感刷新

// 创建axios实例
1
2
3
4
const service = Axios.create({
baseURL,
timeout: 30000
})
// 新增请求拦截器,主要用于请求前带上token
1
2
3
4
5
6
7
8
9
10
11
service.interceptors.request.use(
async config => {
// 设置token
config.headers['token'] = getStorage('token')
return config
},
error => {
// 对请求错误做些什么
return Promise.reject(error)
}
)
// 新增响应拦截器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
service.interceptors.response.use(
async (res) => {
if (res.code === 100) {
// token过期,用refreshToken去刷新token
// 这里我用的自己封装的请求方法,可以忽略
const methodMap = {
get: $get,
post: $post,
put: $put,
del: $del
}
// 处理响应数据,准备重试
const method = methodMap[res.config.method]
const url = res.config.url
let data = res.config.data

// 刷新token
await store.dispatch('system/execRefreshToken')
// 重试(挨个重试,没有存)
return method(url, data)
} else {
...
}

},
err => {
return Promise.reject(err)
}
)
// 刷新部分

如果多个接口用过期的token,都会进入此方法,为了防止重复调用刷新,设置了promise,能适用于大部分情况

注:但是这个promise只能在刷新时防止重复,试想如果有个接口(用了过期token的)响应很慢,在刷新完token后,promise置null了,才进入响应拦截器,这时就又会调用一遍刷新。

TODO:1.promise置null加个延迟,能防止大部分情况。2.刷新后再设置个变量,短时间内用来判断是否已经刷新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let promise = null
execRefreshToken() {
if(promise) return promise
promise = new Promise((resolve, reject) => {
const token = getStorage('token')
const refreshToken = getStorage('refreshToken')
const params = {
token,
refreshToken
}
$post(api.sysLoginRefreshToken, params).then(res => {
setStorage('token', res.token)
setStorage('refreshToken', res.refreshToken)
resolve(res)
}).catch(err => {
reject(err)
})
})
promise.finally(() => {
promise = null
})
return promise
}