背景介绍
在一些项目中,登录机制是双token机制,即有一个短期token(一般设置为30分钟过期),还有一个刷新token(过期时间比较长,可能1天或多天),用于去重新获取新的短期token。
前端的无感刷新技术实现原理主要是,通过axios的拦截器,在响应拦截里获取到后端接口的返回码,根据返回码判断短期token是否过期。
若短期token过期,则去请求判断刷新token是否过期的接口,如果刷新token过期,则返回登陆页面。若刷新token还有效,则用刷新token重新请求接口,刷新获取新的短期token,再去请求刚才的接口。
axios封装案例
import axios from 'axios'// 创建实例时配置默认值
let url = import.meta.env.VITE_BASE_API;let flag = false // 设置开关,保证一次只能请求一次刷新token,防止客户多次操作,多次请求
let subSequest = []// 把过期请求添加在数组中
function addRequest(request) {console.log('把过期请求添加在数组中')subSequest.push(request)
}// 重新调用过期请求列表
function retryRequest() {console.log('重新调用过期请求列表')subSequest.forEach((request) => request())subSequest = []
}// 刷新token
function refreshToken() {if(!flag) {console.log('刷新token')flag = true;// 获取刷新tokenlet r_tk = getRefreshToken()if(r_tk) {// 判断刷新token是否过期refresh().then(res => {// 刷新token失效,退出登录if(res.code === 4006) {flag = false// 移除刷新tokenremoveRefreshToken()} else if(res.code === 2000) {// 存储新的主tokensetAccessToken(res.data.accessToken)// 存储新的刷新tokensetRefreshToken(res.data.refreshToken)flag = false// 重新发送请求retryRequest()}})}}
}// 请求配置
const instance = axios.create({baseURL: `${url}`, // 设置axios全局api URL// baseURL: 'http://30.118.139.106/stg/ai/aigcv2',timeout: 60000, // 默认值是 `0` (永不超时)// `withCredentials` 表示跨域请求时是否需要使用凭证withCredentials: true, // default
});// 添加请求拦截器
instance.interceptors.request.use(function (config) {// 在发送请求之前做些什么config.headers['X-Request-Id'] = getUUID();return config;
}, function (error) {// 对请求错误做些什么return Promise.reject(error);
});// 添加响应拦截器
instance.interceptors.response.use((response) => {// 2xx 范围内的状态码都会触发该函数。const { status, config } = responsereturn new Promise((resolve, reject) => {if (status === 200) {const code = parseInt(response.data.code)if (code === 200) {resolve(response.data)}else if (code === 402) {// toLogin('用户token过期请重新登录~')// 移除失效的主tokenlocalStorage.removeItem('token')// 把过期请求存储起来,用于请求到新的刷新tokenaddRequest(() => resolve(instance(config)))// 用刷新token去请求新的主tokenrefreshToken()}else {resolve(response.data)}}else {resolve(response.data)Message.error(response.data.message || response.data.responseMsg);}})},
(error) => {if (error) {// 登录态失效const { response: { status, data } } = error}return Promise.reject(error);
});export default instance;