前言
在做项目时,我们都会使用到token,而token是客户端登录时由服务端所生成的,后续客户端的每次请求也需携带token给服务端,起到了一个校验用户身份以及安全性的作用。所以token一般都会设置有效期,而根据系统的安全需要,token的有效期一般都会很短,所以会导致用户在操作系统的过程中,会出现突然掉线并跳转到登录页重新登录的情况,用户体验极其不好。所以,此时需要使用双token的方式,实现无感刷新,避免用户频繁重新登录的情况。
实现思路
前端登录时,后端返回两个token,一个有效时间短(token),一个有效时间长(refreshToken),前端拿到两个token,通过vuex存储起来。
-
token有效期短,用于每次对话请求的身份验证; -
refreshToken有效期长,用于每次token过期后对token进行刷新,保证了用户操作过程中不会因为token过期而重新登录,也是实现无感刷新的关键;
流程
最后,token和refreshToken均过期后,用户需重新登录。
代码实现
下面代码源自此大佬博客:www.cnblogs.com/huang-guosh…
// 刷新token的请求函数
function refreshToken() {
return service({
url: '/sys/user/refreshToken',
method: 'post',
})
}
// 因axios请求为异步,故可能会出现同时多次刷新token的情况,该变量相当于给刷新token上了个锁
let isRefreshing = false
// response拦截器,用于处理响应数据
service.interceptors.response.use(
// 响应数据处理成功时的回调函数
response => {
let res = response.data
// 如果响应数据为100101,说明token过期,也要排除掉刷新token的请求
if (response.data.code === 100101 && !response.config.url.includes('/sys/user/refreshToken')) {
//先查询vuex中是否有refreshToken
//如果没有,则直接重定向到登录页面进行重新登录
if (!store.getters.refresh_token) {
router.push('/login')
return response
//如果有,则先判断是否已经有过刷新请求,如果没有,进行刷新请求
} else {
if (!isRefreshing) {
isRefreshing = true
// 获取refreshToken的值
var tokenResult = store.getters.refresh_token
// 构造发送刷新token请求时的请求数据
const tokenVal = {refreshToken:tokenResult}
// 调用刷新token的请求函数
return refreshToken(tokenVal,{withoutToken:true}).then(res => {
//通过该请求响应数据的状态码判断刷新token(refreshToken)是否过期
if (res.code === 100101) {
// 如果刷新token也已过期,则跳转到登录页进行重新登录
router.push('/login')
isRefreshing = false
return res
}else{
//未过期时会得到两个新的token,此时将其持久化
store.dispatch('user/settokenAction', res.data.token)
store.dispatch('user/setrefreshTokenAction', res.data.refreshToken)
// 更新请求头中的token值
response.config.headers.token = res.data.token
// 对于post和put请求,需要将data从字符串类型转换成json格式
if (response.config.method === 'post'||response.config.method === 'put') {
response.config.data=JSON.parse(response.config.data)
}
isRefreshing = false
//重新发起因token过期而未能成功实现的请求
response.config.url = response.config.url.slice(4)
return service.request(response.config)
}
})
}
}
}
},
error => {
if (error.response.status === 500) {
Message({
message: error.response.data.message,
type: "error",
duration: 5 * 1000
});
}
return Promise.reject(error);
}
);
上述内容均源自各位大佬,已附上各大佬文章博客链接,本文仅供本菜鸡记录总结使用,如有侵权,联系必删。