1.采用双token机制【access_token+refresh_token】
a.登录成功之后,返回这两个 token;
b.访问接口时带上 access_token 访问;
c.当 access_token 过期时,通过 refresh_token 来刷新,拿到新的 access_token 和 refresh_token
有了 refresh_token 之后,只要带上这个 token 就能标识用户,不需要传用户名密码就能拿到新 token。
而 access_token 一般过期时间设置的比较短,比如 30 分钟,refresh_token 设置的过期时间比较长,比如 7 天。
这样,只要你 7 天内访问一次,就能刷新 token,再续 7 天,一直不需要登录。
但如果你超过 7 天没访问,那 refresh_token 也过期了,就需要重新登录了
具体执行
1.在响应拦截器中通过response
返回的状态码判断状态,比如401说明token过期
2.如果获取不到【刷新】令牌,说明未登录状态,如果获取到【刷新】令牌则执行刷新令牌操作
执行顺序和逻辑
- 初始请求返回401:首次遇到需要刷新令牌的情况时,将
isRefreshToken
设置为true
并尝试刷新令牌。 - 等待刷新:此时,其他进来的请求检测到
isRefreshToken
为true
,知道令牌正在刷新,因此将自己加入到requestList
队列中,等待令牌刷新完成。 - 令牌刷新成功:一旦令牌刷新成功,执行
requestList.forEach(cb => cb())
,遍历执行队列中的所有请求,这时使用的是新令牌。 - 令牌刷新失败:如果令牌刷新失败,根据具体的错误处理逻辑,可能直接清空队列(避免重复请求)并处理登录状态。
以下为响应拦截器代码
// 响应拦截器
axiosInstance.interceptors.response.use(async res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
const msg = res.data.msg
if (code === 401) {
// 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
if (!isRefreshToken) {
isRefreshToken = true;
// 1. 如果获取不到刷新令牌,则只能执行登出操作
// setTimeout(() =>{},1000)
if (getRefreshToken() == null || getRefreshToken().trim().length == 0) {
isRefreshToken = false
return handleAuthorized();
}
// 2. 进行刷新访问令牌
try {
const refreshTokenRes = await refreshToken()
if (refreshTokenRes.code == 0){
// 2.1 刷新成功,则回放队列的请求 + 当前请求
setToken(refreshTokenRes.data)
requestList.forEach(cb => cb())
return axiosInstance(res.config)
}
} catch (e) {// 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
// 2.2 刷新失败,只回放队列的请求
// requestList.forEach(cb => cb())
// 提示是否要登出。即不回放当前请求!不然会形成递归
requestList = []
isRefreshToken = false
return handleAuthorized();
// return;
} finally {
requestList = []
isRefreshToken = false
}
} else {
// 添加到队列,等待刷新获取到新的令牌
return new Promise(resolve => {
requestList.push(() => {
res.config.headers['Authorization'] = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
resolve(axiosInstance(res.config))
})
})
}
}else if (code !== 200) {
if (msg === '无效的刷新令牌' || msg === '用户未扫码') { // hard coding:忽略这个提示,直接登出
} else {
Notification.error({
title: msg
})
}
return Promise.reject('error')
} else {
return res
}
}, error => {
let {message} = error;
if (message === "Network Error") {
message = "系统正在维护升级中";
} else if (message.includes("timeout")) {
message = "系统接口请求超时";
} else if (message.includes("Request failed with status code")) {
if (message.substr(message.length - 3) === '401'){
return handleAuthorized()
}else {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
}
Message({
message: message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
function handleAuthorized() {
if (!window.location.href.endsWith("loginPage")){//loginPage页面不显示提示
Message({
message: '无效的会话,或者会话已过期,请重新登录。',
type: 'warning',
duration: 1 * 1000
})
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
}
版权声明:《 无感刷新token流程 》为胡光喆原创文章,转载请注明出处!
最后编辑:2024-2-5 03:02:48