前端登录业务中,现在比较常用的是后端返回长短有效期的双token,在我们短token的过期后,使用r长token进行更新短token操作,但是又不想中断用户操作,这个时候就需要调整axios进行token的无感刷新。
根据上述操作我们需要以下步骤:
- 当请求返回token过期的状态码时拦截用户请求
- 拿到长token发起刷新token操作的请求
- 拿到刷新token请求的结果
- 根据结果进行下一步操作:跳转到登录还是恢复调用用户请求
具体的代码实现如下:
const getToken = () => {
const token = localStorage.getItem("token")
if (token) {
return token
}
return ''
}
const requset = axios.create({
baseURL: "/api",
timeout: 5000,
});
// 请求拦截
requset.interceptors.request.use((config) => {
const token = getToken()
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
// 响应拦截
requset.interceptors.response.use(async (res) => {
// 2xx 状态码会执行这里
if (res.status >= 200 && res.status < 300) {
return res.data
}
}, async (err) => {
// 非正常状态
if (err.status == 401) {
console.log("未登录")
// location.href = "/login"
} else if (err.status == 403) {
// 刷新token
const res = await refreshTokenApi()
// 判断刷新token的结果
if (res) {
// 重新发送请求
err.config.headers.Authorization = `Bearer ${getToken()}`
// 等待重新请求结果
const response = await requset.request(err.config)
// 返回重新请求结果
return response
} else {
console.log("刷新token失败")
// location.href = "/login"
return err
}
}
return Promise.reject(err)
})
export default requset
refreshTokenApi的实现如下:
代码中promise
作用是当连续并发多个请求都过期时 只返回一个刷新token的promise, 否则会发起多个请求,造成刷新的token过期陷入死循环
let promise;
const refreshTokenApi = () => {
if (promise) {
return promise
}
promise = new Promise(async (resolve, reject) => {
// 拿到长token
const token = localStorage.getItem("refresh")
// 未取到长token 直接返回失败
if (!token) {
return false
}
// 换取新的token
const res = await request({
url: "/refresh",
method: "post",
data: { refreshToken: token }
})
localStorage.setItem("token", res.data.token)
localStorage.setItem("refresh", res.data.refreshToken)
// 返回token刷新的结果 这里需要根据项目实际返回值确定
resolve(res.code == 1)
})
// 刷新完成后 清空promise
promise.finally(() => {
promise = null
})
return promise
}