|
|
@@ -1,20 +1,74 @@
|
|
|
-import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
|
|
|
-import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
-import { Session } from '/@/utils/storage';
|
|
|
+import axios, { AxiosInstance } from 'axios';
|
|
|
+import { ElMessage } from 'element-plus';
|
|
|
+import { Local, Session } from '/@/utils/storage';
|
|
|
|
|
|
// 配置新建一个 axios 实例
|
|
|
-const service: AxiosInstance = axios.create({
|
|
|
- baseURL: import.meta.env.VITE_API_URL,
|
|
|
+export const service = axios.create({
|
|
|
+ baseURL: import.meta.env.VITE_API_URL as any,
|
|
|
timeout: 50000,
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
});
|
|
|
|
|
|
+// token 键定义
|
|
|
+export const accessTokenKey = 'access-token';
|
|
|
+export const refreshAccessTokenKey = `x-${accessTokenKey}`;
|
|
|
+
|
|
|
+// 获取 token
|
|
|
+export const getToken = () => {
|
|
|
+ return Local.get(accessTokenKey);
|
|
|
+};
|
|
|
+
|
|
|
+// 清除 token
|
|
|
+export const clearAccessTokens = () => {
|
|
|
+ Local.remove(accessTokenKey);
|
|
|
+ Local.remove(refreshAccessTokenKey);
|
|
|
+
|
|
|
+ // 清除其他
|
|
|
+ Session.clear();
|
|
|
+
|
|
|
+ // 刷新浏览器
|
|
|
+ window.location.reload();
|
|
|
+};
|
|
|
+
|
|
|
+// axios 默认实例
|
|
|
+export const axiosInstance: AxiosInstance = axios;
|
|
|
+
|
|
|
// 添加请求拦截器
|
|
|
service.interceptors.request.use(
|
|
|
- (config: AxiosRequestConfig) => {
|
|
|
- // 在发送请求之前做些什么 token
|
|
|
- if (Session.get('token')) {
|
|
|
- config.headers!['Authorization'] = `${Session.get('token')}`;
|
|
|
+ (config) => {
|
|
|
+ // // 在发送请求之前做些什么 token
|
|
|
+ // if (Session.get('token')) {
|
|
|
+ // (<any>config.headers).common['Authorization'] = `${Session.get('token')}`;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // 获取本地的 token
|
|
|
+ const accessToken = Local.get(accessTokenKey);
|
|
|
+ if (accessToken) {
|
|
|
+ // 将 token 添加到请求报文头中
|
|
|
+ config.headers!['Authorization'] = `Bearer ${accessToken}`;
|
|
|
+
|
|
|
+ // 判断 accessToken 是否过期
|
|
|
+ const jwt: any = decryptJWT(accessToken);
|
|
|
+ const exp = getJWTDate(jwt.exp as number);
|
|
|
+
|
|
|
+ // token 已经过期
|
|
|
+ if (new Date() >= exp) {
|
|
|
+ // 获取刷新 token
|
|
|
+ const refreshAccessToken = Local.get(refreshAccessTokenKey);
|
|
|
+
|
|
|
+ // 携带刷新 token
|
|
|
+ if (refreshAccessToken) {
|
|
|
+ config.headers!['X-Authorization'] = `Bearer ${refreshAccessToken}`;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // debugger
|
|
|
+ // get请求映射params参数
|
|
|
+ if (config.method?.toLowerCase() === 'get' && config.data) {
|
|
|
+ let url = config.url + '?' + tansParams(config.data);
|
|
|
+ url = url.slice(0, -1);
|
|
|
+ config.data = {};
|
|
|
+ config.url = url;
|
|
|
+ }
|
|
|
}
|
|
|
return config;
|
|
|
},
|
|
|
@@ -26,24 +80,61 @@ service.interceptors.request.use(
|
|
|
|
|
|
// 添加响应拦截器
|
|
|
service.interceptors.response.use(
|
|
|
- (response) => {
|
|
|
- // 对响应数据做点什么
|
|
|
- const res = response.data;
|
|
|
- if (res.code && res.code !== 0) {
|
|
|
- // `token` 过期或者账号已在别处登录
|
|
|
- if (res.code === 401 || res.code === 4001) {
|
|
|
- Session.clear(); // 清除浏览器全部临时缓存
|
|
|
- window.location.href = '/'; // 去登录页
|
|
|
- ElMessageBox.alert('你已被登出,请重新登录', '提示', {})
|
|
|
- .then(() => {})
|
|
|
- .catch(() => {});
|
|
|
- }
|
|
|
- return Promise.reject(service.interceptors.response);
|
|
|
- } else {
|
|
|
- return response.data;
|
|
|
+ (res) => {
|
|
|
+ // 获取状态码和返回数据
|
|
|
+ var status = res.status;
|
|
|
+ var serve = res.data;
|
|
|
+
|
|
|
+ // 处理 401
|
|
|
+ if (status === 401) {
|
|
|
+ clearAccessTokens();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理未进行规范化处理的
|
|
|
+ if (status >= 400) {
|
|
|
+ throw new Error(res.statusText || 'Request Error.');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理规范化结果错误
|
|
|
+ if (serve && serve.hasOwnProperty('errors') && serve.errors) {
|
|
|
+ throw new Error(JSON.stringify(serve.errors || 'Request Error.'));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 读取响应报文头 token 信息
|
|
|
+ var accessToken = res.headers[accessTokenKey];
|
|
|
+ var refreshAccessToken = res.headers[refreshAccessTokenKey];
|
|
|
+
|
|
|
+ // 判断是否是无效 token
|
|
|
+ if (accessToken === 'invalid_token') {
|
|
|
+ clearAccessTokens();
|
|
|
+ }
|
|
|
+ // 判断是否存在刷新 token,如果存在则存储在本地
|
|
|
+ else if (refreshAccessToken && accessToken && accessToken !== 'invalid_token') {
|
|
|
+ Local.set(accessTokenKey, accessToken);
|
|
|
+ Local.set(refreshAccessTokenKey, refreshAccessToken);
|
|
|
}
|
|
|
+
|
|
|
+ // 响应拦截及自定义处理
|
|
|
+ if (serve.code === 401) {
|
|
|
+ clearAccessTokens();
|
|
|
+ } else if (serve.code === undefined) {
|
|
|
+ return Promise.resolve(res);
|
|
|
+ } else if (serve.code !== 200) {
|
|
|
+ var message = JSON.stringify(serve.message);
|
|
|
+ ElMessage.error(message);
|
|
|
+ throw new Error(message);
|
|
|
+ }
|
|
|
+
|
|
|
+ return res;
|
|
|
},
|
|
|
(error) => {
|
|
|
+ // 处理响应错误
|
|
|
+ if (error.response) {
|
|
|
+ if (error.response.status === 401) {
|
|
|
+ clearAccessTokens();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 对响应错误做点什么
|
|
|
if (error.message.indexOf('timeout') != -1) {
|
|
|
ElMessage.error('网络超时');
|
|
|
@@ -53,9 +144,57 @@ service.interceptors.response.use(
|
|
|
if (error.response.data) ElMessage.error(error.response.statusText);
|
|
|
else ElMessage.error('接口路径找不到');
|
|
|
}
|
|
|
+
|
|
|
return Promise.reject(error);
|
|
|
}
|
|
|
);
|
|
|
|
|
|
+/**
|
|
|
+ * 参数处理
|
|
|
+ * @param {*} params 参数
|
|
|
+ */
|
|
|
+export function tansParams(params: any) {
|
|
|
+ let result = '';
|
|
|
+ for (const propName of Object.keys(params)) {
|
|
|
+ const value = params[propName];
|
|
|
+ var part = encodeURIComponent(propName) + '=';
|
|
|
+ if (value !== null && value !== '' && typeof value !== 'undefined') {
|
|
|
+ if (typeof value === 'object') {
|
|
|
+ for (const key of Object.keys(value)) {
|
|
|
+ if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {
|
|
|
+ let params = propName + '[' + key + ']';
|
|
|
+ var subPart = encodeURIComponent(params) + '=';
|
|
|
+ result += subPart + encodeURIComponent(value[key]) + '&';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ result += part + encodeURIComponent(value) + '&';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 解密 JWT token 的信息
|
|
|
+ * @param token jwt token 字符串
|
|
|
+ * @returns <any>object
|
|
|
+ */
|
|
|
+export function decryptJWT(token: string): any {
|
|
|
+ token = token.replace(/_/g, '/').replace(/-/g, '+');
|
|
|
+ var json = decodeURIComponent(escape(window.atob(token.split('.')[1])));
|
|
|
+ return JSON.parse(json);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 将 JWT 时间戳转换成 Date
|
|
|
+ * @description 主要针对 `exp`,`iat`,`nbf`
|
|
|
+ * @param timestamp 时间戳
|
|
|
+ * @returns Date 对象
|
|
|
+ */
|
|
|
+export function getJWTDate(timestamp: number): Date {
|
|
|
+ return new Date(timestamp * 1000);
|
|
|
+}
|
|
|
+
|
|
|
// 导出 axios 实例
|
|
|
-export default service;
|
|
|
+export default service;
|