axios无感知刷新token
前言
在写自己的小项目的时候,我后台用到了JWT,登录后会生成一个accessToken
和refreshToken
。accessToken
用来进行认证,但是有一个失效时间,如果失效了会用refreshToken
来换取新的accessToken
。如果在用户正在访问网站的过程中accessToken
失效,怎样实现后台自动刷新accessToken
,而不会导致退出登录呢?
我的小项目的前端是用vue+axios来写的。接下来我们就开始吧~
思路
-
在
accessToken
过期时,我的后台服务器返回的状态码是403。所以我们需要在axios的响应拦截器中处理。 -
判断响应状态码是403的时候,我们先把当前的请求放到一个队列中保存起来,当token刷新完毕之后,再重新请求。我们可以创建一个Pormise对象,当Pormise对象的resolve方法未被调用时,会处于Pending状态。
-
上边说到把请求放到队列中保存,是因为,如果请求的比较频繁,在刷新token接口还没有返回结果的时候,又来了一个请求,那么这个新的请求也应该等待,在刷新token接口返回结果后,统一重新请求。
-
这里我们还应该考虑到一种情况,就是请求比较频繁,在刷新token接口还没有返回结果的时候,又来了一个请求,这时候不应该再去刷新token。所以我们要用一个变量来标识当前是否在刷新token。
-
执行完重新请求后,要将缓存的请求在队列中清空,在刷新完token后将标识置反。
代码
下面是完整的代码:
let isRefreshing = false // 是否正在刷新token
let callbacks = [] // 失效后同时发送请求的容器 -- 缓存接口
// 刷新 token 后, 将缓存的接口重新请求一次
function onAccessTokenFetched(newToken) {
callbacks.forEach(callback => {
callback(newToken)
})
// 清空缓存接口
callbacks = []
}
// 添加缓存接口
function addCallbacks(callback) {
callbacks.push(callback)
}
// 响应拦截器
instance.interceptors.response.use(
response => {
if (response.data.code !== 200){
MessageError(response.data.message)
}
return response.data
},
error => {
/**
* 将未授权接口缓存起来。retryOriginalRequest 这个 Promise 函数很关键,它一直处于等待状态。
* 只有当token刷新成功后,onAccessTokenFetched 这个函数执行了回调函数,返回了 resolve 状态
*/
if (error.response && error.response.status === 403){
// 获取当前的请求
let config = error.response.config
//
const retryOriginalRequest = new Promise(resolve => {
addCallbacks(newToken => {
// 表示用新的token去替换掉原来的token
config.headers.Authorization = newToken
resolve(instance.request(config)) // 调用resolve请求队列里面接口
})
})
// 无感刷新Token
if (!isRefreshing) {
isRefreshing = true
instance.post('/token/refresh', {refreshToken: getRefreshToken()}).then(response => { // 用refreshToken获取新的token
let accessToken = response.data.accessToken
let refreshToken = response.data.refreshToken
setAccessToken(accessToken)
setRefreshToken(refreshToken)
onAccessTokenFetched(accessToken)
}).catch(() => {
// 刷新token错误跳转到登陆页面
removeToken()
router.push('/login')
}).finally(() => {
isRefreshing = false
})
}
return retryOriginalRequest // 将token过期期间请求的接口包装成promise返回,等待刷新token后重新请求
}else{
MessageError(error.response.data.message)
return Promise.reject(new Error(error.message || 'Error'))
}
}
)
axios无感知刷新token
https://www.zhaojun.inkhttps://www.zhaojun.ink/archives/1019