最近在做项目项目中需要无感刷新token 最近遇到了这个问题
思路
- 登录时获取token和用于重新获取新token的刷新refresh_token
- token过期接口返回错误状态码,并将此接口保存起来,让返回结果处于pending中(token过期的接口在响应拦截中不返回结果)
- 调接口刷新token 并记录在刷新token 此时调用的接口也保存起来,让返回结果处于pending中
- 重新请求token过期的接口
上代码
axios 封装文件
// axios/axios.ts
import { responseInterceptors401 } from '@/axios/index';
responseInterceptors401 &&
响应拦截器
this.axiosInstance.interceptors.response.use((res: any) => {
//axios方法,res接口返回结果 this.options请求配置
return responseInterceptors401(axiosInstance, res, this.options);
}, undefined);
//settings.index.ts
// 放行接口配置文件
export const REFRESH_TOKEN_API = '/uaa-service/oauth/token';
export const LoginOUt_API = '/uaa-service/oauth/loginOut';
//axios/index.ts
import type { AxiosInstance } from 'axios';
import { LoginOUt_API, REFRESH_TOKEN_API } from '@/settings';
import { useUserStoreWithOut, getToken } from '@/store/modules/user';
//只处理接口返回code===401(token过期)拦截器
/**
* @param axios方法
* @param res接口返回结果
* @param options请求配置
*/
export const responseInterceptors401 = async (axiosInstance: AxiosInstance, res: any, options) => {
const { config, data } = res;
const apiUrl = options.requestOptions?.apiUrl || '';
if (data.code !== 401) return res;
// 放行 刷新token和登出接口,刷新token接口也可能返回401,登出接口也可能401(我们的后端的确返回了所以限制一下)
if ([apiUrl + LoginOUt_API, apiUrl + REFRESH_TOKEN_API].includes(config.url)) return res;
const userStore = useUserStoreWithOut();
//将请求接口保存起来 接口返回401 且不是登录和刷新token接口都存起来
const token = getToken();
const promise = new Promise((resolve) => {
// 更换token
config.headers.Authorization = options.authenticationScheme
? `${options.authenticationScheme} ${token}`
: token;
userStore.setRequests(() => resolve(axiosInstance(config)));
});
// 判断是否正在调用刷新token接口,没有就调用
if (!userStore.isRefreshing) {
await userStore.getNewToken();
// 调用成功调用报讯接口
userStore.requests.forEach((cb) => {
cb();
});
// 接口掉完 清空保存接口数组
userStore.requests = [];
}
return promise;
};
//store/modules/user.ts
interface UserState {
token?: string;
refreshToken?: string;
isRefreshing: boolean; // 是否正在刷新token
requests: Array<any>; //刷新token缓存请求信息
}
export const useUserStore = defineStore({
id: 'user',
state: (): UserState => ({
token: undefined,
refreshToken: undefined,
isRefreshing: false,
requests: [],
}),
actions: {
/**
* 设置token
* @param info
*/
setToken(info: string | undefined) {
this.token = info ? info : ''; // for null or undefined value
},
/**
* 设置刷新token
* @param info
*/
setRefreshToken(info: string | undefined) {
this.refreshToken = info ? info : ''; // for null or undefined value
},
/**
* 缓存请求 axiosInstance, error
* @param requests
*/
setRequests(requests: any) {
this.requests.push(requests);
},
/**
* 设置是否请求种状态
* @param flag
*/
setIsRefreshing(flag: boolean) {
this.isRefreshing = flag;
},
/**
* 刷新token
*/
async getNewToken() {
if (!this.isRefreshing) {
this.setIsRefreshing(true);
//刷新token参数
const params: any = {
refresh_token: this.refreshToken,
};
//刷新token接口 接口封装代码就不放了,每个项目封装各不相同
const data = await API.refreshToken(params);
if (data) {
const { accessToken, refreshToken } = data;
//设置新token和refreshToken
this.setToken(accessToken);
this.setRefreshToken(refreshToken);
// 更改是否正在调用刷新接口状态
this.setIsRefreshing(false);
}
}
},
},
});
export function useUserStoreWithOut() {
return useUserStore(store);
}