有人会说token刷新有什么意义?后端自动延长过期时间不就行了?这只是增加前端工作量而已!!!想到这,我已决定不在低调,“怒从心头起,恶向胆边生”,直接去找客户说道说道。其实我觉得token刷新还是有用的,万一token被人劫持了呢???所以我决定听客户这一次,实现token无感刷新这一需求(这和客户是1米8的健壮大哥没有半毛钱关系,哎,“万般皆是命,半点不由人”),经过一顿疯狂操作,牺牲寿命,终于把需求实现了。
思路
判断token是否过期,如已过期,则发起请求重新刷新
实现
import axios from "axios";
import router from "@/router";
import qs from 'qs';
//需要用到的方法
import { getRefreshToken, setLoginTime, getLoginTime, getExpiresTime } from "./oauth";
// 定义一个状态,用于判断token是否正在刷新,防止重复刷新
let isRefreshing = false;
// 在token刷新时,是否有别的请求,把它们保存起来,token刷新完后在重新调用
let refreshSubrequest = [];
// 创建axios 实例
const service = axios.create({
baseURL: serviceConfig.commonConfig.baseURL,
timeout: appConfig.timeout,
headers: {
"Content-Type": "application/jsonapplication/x-www-form-urlencoded"
}
});
//再创建一个axios实例,单独调用token刷新的接口,防止和其他请求配置相互影响,有大佬知道别的方法可以告诉我..........
const serviceToken = axios.create({
baseURL: serviceConfig.commonConfig.baseURL,
timeout: appConfig.timeout,
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
"Authorization": "xxxxxxxxxxxx" //项目需要无token时,请求带特定的token,和token刷新没关系
}
});
//由于功能写在响应拦截中,因此此处就不写具体的请求拦截器代码了
service.interceptors.request.use()
//响应拦截器 他来了,他来了
service.interceptors.response.use(response=>{
//定义变量,用于判断token是否到过期时间
//getExpiresTime方法为获取token有效期
//getLoginTime方法为获取上次token获取时间,在后端token失效时间的十分钟之前刷新token
let expired = (new Date().getTime() - getLoginTime()) > (getExpiresTime - 10) * 60 * 1000;
//在登录页不需要刷新token
if (router.currentRoute.path !== "/Login") {
if (expired) {
//记录response配置,
const config = response.config;
//是否已在刷新中
if (!isRefreshing) {
isRefreshing = true;
//发起token刷新请求,用的另一个实例
return serviceToken.post('/auth-svc/oauth/token',
qs.stringify( //qs为项目需要
{
grant_type: 'refresh_token',
refresh_token: getRefreshToken()
}
)
).then(res => {
const data = res.data.data;
const { token } = data;
//将请求来的信息存储到本地
for(let item in data){
window.sessionStorage.setItem(item,data[item]);
}
window.sessionStorage.setItem("token",'Bearer' + " " + data.access_token);
//更新token刷新时间
setLoginTime();
//项目需要token放在Authorization中
config.headers['Authorization'] = token;
config.baseURL = '';
//将储存的请求执行
refreshSubrequest.forEach(item => item(token));
//清空请求
refreshSubrequest = [];
return service(config);
}).catch(res => {
console.error('refreshtoken error =>', res);
router.replace({
path: '/Login'
})
}).finally(() => {
isRefreshing = false;
});
} else {
// 正在刷新token,返回一个未执行resolve的promise
return new Promise((resolve) => {
// 将resolve放进队列,用一个函数形式来保存,等token刷新后直接执行
refreshSubrequest.push((token) => {
config.baseURL = '';
config.headers['Authorization'] = token;
resolve(service(config));
});
});
}
}
}
return response
})
所用到的方法
const ExpiresKey = 'expires_in'; //注意: 此时间为token有效时长
const TokenTypeKey = 'bearer';
const RefreshTokenKey = 'refresh_token';
const loginTimeKey = "loginTime";
/**
* 获取刷新token的参数
* @returns 刷新token的参数
*/
export function getRefreshToken () {
return sessionStorage.getItem(RefreshTokenKey);
}
/**
* 保存登录时间戳
*/
export function setLoginTime(){
const loginTime = new Date().getTime();
sessionStorage.setItem(loginTimeKey,loginTime);
}
/**
* 获取上次登录时间戳
* @returns 上次登录时间戳
*/
export function getLoginTime(){
return sessionStorage.getItem(loginTimeKey);
}
/**
* 获取token有效期
* @returns token有效期
*/
export function getExpiresTime(){
return sessionStorage.getItem(ExpiresKey);
}