request.js
import axios from 'axios'
import { apiUrl } from '@/config/global_config'
import refreshToken from './refreshToken'
//前缀
const service = axios.create({
baseURL: apiUrl,
timeout: 5000 // 请求超时时间
})
//请求
service.interceptors.request.use(
config => {
return config
},
error => {
console.log(error)
return Promise.reject(error)
}
)
//响应
service.interceptors.response.use(
response => {
const data = response.data
if (data.code === 200) {
return Promise.resolve(data)
} if (data.code === 401) {
refreshToken(response)
} else {
let err = {}
let messInfo = data.message ? err.info + ' ' + data.message : err.info
return Promise.reject(new Error(messInfo || 'Error'))
}
},
err => {
console.log('响应错误:' + err)
return Promise.reject(err) //请求错误时,直接结束
}
)
// 封装通用的接口调用方法
export default (options) => {
return service({
method: options.method || 'GET',
url: options.url,
// ES6规则:对象的key可以是动态的变量
[options.method.toUpperCase() === 'GET' ? 'params' : 'data']: options.data
})
}
refreshToken.js
import { useRouter } from 'vue-router';
import request from './request';
import { ElMessage } from 'element-plus'
const MAX_ERROR_COUNT = 5; //最大请球次数
let currentCount = 0; //当前请求了多少次
const queue = []; //把阻塞的请求储存到数组里一次请求玩
let isRefresh = false;
const { router } = useRouter()
export default async function refreshToken(error) {
// 退出登录
const logout = () => {
ElMessage.error('身份过期,请重新登录')
router.replace('/login')
// 清空数据 token的数据
// Session.clear();
localStorage.clear()
return Promise.reject(error);
};
if (error.config.url?.includes('refresh')) {
// 如果url中包含refresh 退出登录
logout();
}
const refresh = localStorage.getItem('refresh') ?? null;
const { config } = error;
if (!refresh) { //如果没找到替换的refreshtoken退出登录
logout();
}
// 判断当前是否为刷新状态中(防止多个请求导致多次调refresh接口)
if (!isRefresh) {
// 设置当前状态为刷新中
isRefresh = true;
// 如果重发次数超过,直接退出登录
if (currentCount > MAX_ERROR_COUNT) {
logout();
}
// 增加重试次数
currentCount += 1;
try {
const {
data: { access },
} = await UserAuthApi.refreshToken(refresh); //用refreshToken刷新token
localStorage.setItem('token', access) //把新token存到本地
// 重置重发次数
currentCount = 0;
// 遍历队列,重新发起请求
queue.forEach((cb) => cb(access));//用新token吧阻塞的接口全部重新请求
// 返回请求数据
return request(error.config);
} catch {
// 刷新token失败,直接退出登录
ElMessage.error('请重新登录')
localStorage.clear()
router.replace('/login')
return Promise.reject(error);
} finally {
// 重置状态
isRefresh = false;
}
} else {
// 当前正在尝试刷新token,先返回一个promise阻塞请求并推进请求列表中
return new Promise((resolve) => {
// 缓存网络请求,等token刷新后直接执行
queue.push((newToken) => {
Reflect.set(config.headers, 'authorization', newToken);
resolve(request(config));
});
});
}
}```