token何时刷新问题,一直都困扰着我,以前也有朋友给我一些解决方案,但始终都不满意,如:1.定时刷新token(10分钟刷一次,只要比过期时间短),2.token有效期延长(十几天)。这或多或少都存在着一些问题,直到这些天我才找到和自己想法类似的文章,并加以修改,分享给你们。
这是请求与响应的过程:
1.前端发起ajax请求
2.后端发现token已经过期,返回相应的状态码
3.前端拦截响应数据,并发起刷新token的请求
4.拿到最新的token,保存到本地
5.拿到最新的token去进行刚刚未请求成功的接口
6.获取到刚刚请求的结果,覆盖第一次请求失败的响应数据
下面是解决何时刷新token的核心方法,写在main.js文件中
//用于重新发送上一条请求
axios.request(response.config);
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import axios from 'axios';
import store from './store/index';
import common from './utils/common.js';
import api from './utils/api.js';
import ElementUI from 'element-ui';
// 堆代码 duidaima.com
...省略一些代码
// 是否正在刷新的标记
let isRefreshing = false;
// 每一项将是一个待执行的函数形式
let requests = [];
/* 添加一个返回拦截器 */
axios.interceptors.response.use(async (response) => {
// 对返回的数据进行一些处理
// console.log(router.currentRoute.fullPath);
console.log(response.data);
if (response.data.code === "1001") {
/* accessToken失效 */
response.config.headers.refreshToken = localStorage.getItem('refreshToken');
/* 利用refreshToken刷新accessToken */
if (!isRefreshing) {
isRefreshing = true;
//刷新token方法,等待刷新token方法执行返回后在执行后面的代码
const refreshToken = await api.api.postRefreshToken({});
/* 重新赋值accessToken */
localStorage.setItem(
"Authorization",
"Bearer " + refreshToken.data.accessToken
);
/* 重新发送上一条请求(这条请求不会再下的else代码中拦截),await不需要加,无需异步完成以后在执行后面函数 */
axios.request(response.config);
requests.forEach(request => request());
requests = [];
isRefreshing = false;
} else {
// 正在刷新token,将返回一个未执行resolve的promise
// 拦截其他所有的请求,使其处于一个未执行的状态,只有当刷新token请求返回以后,再执行拦截的所有请求
return new Promise((resolve) => {
// 将resolve放进数组,用一个函数形式来保存,等token刷新后直接执行
requests.push(() => {
resolve(axios.request(response.config))
})
})
}
} else if (response.data.code === "1002") {
/* refreshToken失效 这个code需要再修改 */
if (!isRefreshing) {
isRefreshing = true;
ElementUI.MessageBox.confirm('登录时间过长,请重新登录?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
/* 跳转到登录页,而后登录成功,还返回当前页 */
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
})
isRefreshing = false;
}).catch(() => {
/* 不做处理,只是为了去除页面上的报错 */
});
}
} else {
return response;
}
},
(error) => {
/* 对返回的错误进行一些处理 */
return Promise.reject(error);
});
大致的思路就如代码中所写的,具体情况还要看你们自己后端是如何实现,如1.请求时发现token过期时只判断accessToken过期,不判断refreshToken过期,只有在刷新accessToken的时候才判断refreshToken,从而进行重新登录。2.在刷新accessToken的时候,refreshToken刷不刷,是否要一段时间后需要重新登录,还是用户活跃一直不需要重新登录,看你们自己的需求与考虑。
以上代码,解决token何时何地,无痛刷新问题,解决并发请求多次刷新token问题,解决refreshToken过期,重新登录问题并跳转到登录前页面,也解决了每个组件都判断token过期问题,删除了大量的重复代码。
理解async 函数,promise对象中pending状态的特性,才是理解本文的关键。测试代码就不贴了,亲测有效,有问题再留言咯。