功能描述
登录系统后会有新的token和refreshToken,token会在一定时间后失效,这时就需要拦截当前的所有请求,先通过refreshToken刷新token,如果刷新成功,将获取到新的token和refreshToken,再将拦截的请求重新使用新的token请求一遍;如果刷新失败说明该token已完全过期就需要退出到登录页面重新登录。
axios.js
import request from "xxx-axios"; //自己封装的axios
import store from "@/store";
import md5 from "js-md5";
import { refreshToken } from "@/api/user";
import i18n from "@/i18n";
import { EN_US, ZH_CN } from "@/constant";
const appId = "b285bbc244c64bec";
let shouldRefresh = false;
let requestConfig = [];
//所有请求都需携带以下header的内容
export function getHeaders(params = {}) {
const { locale } = i18n.global;
const timestamp = new Date().getTime(); //时间戳
const nonce = parseInt((Math.random() * 9 + 1) * 10000); //5位随机数
let prefix = "";
//将请求接口url携带的参数拼接起来作为前缀
Object.keys(params).forEach((key) => (prefix += key + "=" + (params[key] + "")?.trim()));
return {
appId,
authorization: store?.state.user.token,
timestamp,
nonce,
sign: md5(`${prefix + appId + store.state.user.token + timestamp + nonce}`), //通过MD5生成签名
locale
};
}
request.config({
timeout: 30000,
baseURL: process.env.VUE_APP_BASE_URL // "/api"
});
request.instance.interceptors.request.use(
async function (config) {
config.headers = getHeaders(getParams(config) || {});
return config;
},
function (error) {
return Promise.reject(error);
}
);
//关键实现
["get", "post", "put", "delete"].forEach((name) => {
let fn = request[name];
const refreshTokenUrl = "/oauth/token/refresh";
request[name] = (url, ...args) => {
const refresh = () => {
return new Promise((resolve, reject) => {
requestConfig.push((cancel = true) => {
if (!cancel) {
reject("取消");
return;
}
fn.call(null, url, ...args)
.then(resolve)
.catch(reject);
});
});
};
if (shouldRefresh && url !== refreshTokenUrl) {
return refresh();
}
return new Promise((resolve, reject) => {
fn.call(null, url, ...args)
.then((res) => {
if (!shouldRefresh || url === refreshTokenUrl) {
return resolve(res);
}
requestConfig.push((cancel = true) => {
if (!cancel) {
reject("取消");
return;
}
fn.call(null, url, ...args)
.then(resolve)
.catch(reject);
});
})
.catch(reject);
});
};
});
function refreshCookie() {
if (!shouldRefresh) {
shouldRefresh = true;
if (store.state.user.refreshToken && store.state.user.token) {
refreshToken(store.state.user.refreshToken).then((res) => {
shouldRefresh = false;
if (res.success) {
store.commit("user/userLogin", { ...res.data }); //更新token和refreshToken
requestConfig.forEach((fn) => fn()); //接口依次请求
} else {
//刷新也失效
store.dispatch("user/logout"); //登出
requestConfig.forEach((fn) => fn(false)); //接口依次取消
}
requestConfig = [];
});
} else {
//没有refreshToken和token直接登出
store.dispatch("user/logout");
requestConfig.forEach((fn) => fn(false));
requestConfig = [];
shouldRefresh = false;
}
}
}
request.instance.interceptors.response.use(
function (response) {
const { t } = i18n.global;
if (response.status !== 200) {
toast.error(t(`tooltip.networkError`));
} else {
// 下载文件用
if (
(response.request && response.request.responseType === "blob") ||
response.data.code === undefined
) {
if (response.data?.type != "application/octet-stream") {
refreshCookie();
}
return response;
}
// code 数字转换
response.data.code = Number(response.data.code) || response.data.code;
//15101 13101 都是后端返回token需要刷新的code
if (response.data.code === 15101 || response.data.code === 13101) {
refreshCookie();
} else if (response.data.success !== undefined && !response.data.success) {
if (response.config.url != "/oauth/logout")
toast.error({ content: response.data.message });
checkError(response);
return Promise.reject(response.data);
}
return response.data;
}
},
function (error) {
return Promise.reject(error);
}
);
function getParams(config) {
let params = {};
let arrStr = config.url?.split("?") || [];
// if (config.method === "get") { //??todo
params = {
...(config.params || {})
};
// }
if (arrStr.length > 1) {
arrStr[1].split("&").map((m) => {
let temp = m.split("=");
if (temp.length === 2) {
params[temp[0]] = temp[1];
}
});
}
return paramsSort(params);
}
//字典排序
function paramsSort(params) {
let result = {};
let arr = Object.keys(params);
if (arr.length) {
arr.sort();
arr.forEach((key) => (result[key] = params[key]));
}
return result;
}
//检查接口返回的message语言是否是当前系统的语言
function checkError(response) {
const { locale } = i18n.global;
let result = false;
if (response.data?.message) {
try {
let regZh = new RegExp("[\\u4E00-\\u9FFF]+", "g");
result =
(locale == ZH_CN && !regZh.test(response.data?.message)) ||
(locale == EN_US && regZh.test(response.data?.message));
result
? console.error("语言环境错误:", {
url: response?.config?.url,
locale,
message: response?.data?.message,
code: response?.data?.code
})
: null;
} catch (e) {
console.log(e);
}
}
}
export default request;