双token前端版本(token过期 拿 refreshtoken 获取新的token,未发送的请求在获取新的token后再发起) ,全部复制干净可以直接用,多余的逻辑可以自己删除,状态code 处理要看后端那边的定义的是什么. 消息提示的话 看用的什么框架也要改一下
request.js
import { showToast } from '@nutui/nutui';
import { isRefreshRequest } from '@/api/login';
import { getToken } from '@/utils/auth';
import { useUserStore } from '@/store/modules/user';
import router from '@/router';
import { showConfirmDialog } from 'vant';
// 创建 Axios 实例
const service: AxiosInstance = axios.create({
withCredentials: false,
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 10000, // 超时时间
});
// 请求拦截器
service.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const notRequireToken = (config.headers || {}).isToken === false;
if (!notRequireToken && !isRefreshRequest(config) && getToken('accessToken')) {
config.headers['Authorization'] = 'Bearer ' + getToken('accessToken');
}
return config;
},
(error: AxiosError) => {
return Promise.reject(error);
},
);
// 响应拦截器
service.interceptors.response.use(
async (res: AxiosResponse) => {
const code = res.data.code;
if (code && code !== 0 && code !== 1) {
if (code === 450) {
if (isRefreshRequest(res.config)) {
//如果刷新token也过期了
console.warn('refreshToken expired ');
return Promise.resolve('无效的会话,或者会话已过期,请重新登录。');
} else {
const isSuccess = await useUserStore().refreshToken();
if (isSuccess) {
res.config.headers.Authorization = `Bearer ${getToken('accessToken')}`;
return await service(res.config);
} else {
//刷新失败,到登录页
showConfirmDialog({
title: '系统提示',
message: '登录状态已过期,您可以继续留在该页面,或者重新登录',
cancelButtonText: '取消',
confirmButtonText: '重新登录',
theme: 'round-button',
})
.then(() => {
// on confirm
router.push(`/login`); // 否则全部重定向到登录页
})
.catch(() => {
// on cancel
});
return Promise.reject('无效的会话,或者会话已过期,请重新登录。');
}
}
} else {
showToast.text(`请求出错[${res.data.code}]:${res.data.message}`);
res.data.api = res.config.url;
return Promise.reject(res.data);
}
}
if (code === 1) {
//控制台打印警告信息
console.warn('[Request warn]: ' + res.data.message);
// ElMessage({ message: res.data.message, type: "warning" })
}
return res.data;
},
(error: AxiosError) => {
let message = '系统错误';
if (error.message === 'Network Error') {
message = '服务器连接异常';
} else if (error.message.includes('timeout')) {
message = '请求服务器超时';
} else if (error.message.includes('Request failed with status code')) {
if (error.response?.status === 401) {
message = '没有权限访问';
router.push('/login');
} else {
message = `系统接口异常 ${error.message.substr(error.message.length - 3)}`;
}
}
showToast.text(message);
return Promise.reject(error);
},
);
interface RequestConfig {
url: string;
data?: object;
method?: string;
params?: object;
isUploadFile?: boolean;
isDownloadFile?: boolean;
downloadMeta?: boolean;
baseURL?: string;
timeout?: number;
headers?: object;
}
// 封装请求函数
export function request({ url, data, method, params, baseURL, timeout, headers }: RequestConfig) {
return service({
url: url,
method: method ?? 'get',
data: data ?? {},
params: params ?? {},
baseURL: baseURL,
timeout: timeout ?? import.meta.env.VITE_APP_API_TIMEOUT,
headers: headers ?? {},
// 可以根据需要添加自定义处理逻辑
});
}
export default service;
函数import { isRefreshRequest } from '@/api/login';
return !!config.headers?.__isRefreshToken;
}
import { getToken } from '@/utils/auth';
const { VITE_TOKEN_KEY } = import.meta.env;
export function getToken(tokenKey) {
const token = localStorage.getItem(`${VITE_TOKEN_KEY}-${tokenKey}`);
return isEmpty(token) ? '' : token;
}
export function setToken(tokenKey, token) {
return localStorage.setItem(`${VITE_TOKEN_KEY}-${tokenKey}`, token);
}
export function removeToken(tokenKey) {
return localStorage.removeItem(`${VITE_TOKEN_KEY}-${tokenKey}`);
}
import { isEmpty } from '@/utils/validate';
// null or undefined
if (val == null) return true;
if (typeof val === 'boolean') return false;
if (typeof val === 'number') return false;
switch (Object.prototype.toString.call(val)) {
// String or Array
case '[object String]':
return val === 'undefined' || val === 'null' || val.trim() === '';
case '[object Array]':
return !val.length;
// Map or Set or File
case '[object File]': {
return val.size === 0 || val.type === '';
}
case '[object Map]': {
return !val.size;
}
case '[object Set]': {
return !val.size;
}
// Plain Object
case '[object Object]': {
return !Object.keys(val).length;
}
}
return false;
}
公共状态库import { useUserStore } from '@/store/modules/user';
import { defineStore } from 'pinia';
import router from '@/router';
import { setToken, getToken, removeToken } from '@/utils/auth';
import defAva from '@/assets/images/default-avatar.png';//图片可删
import { loginWxWork, refreshToken, login, logout } from '@/api/login';//api
import { getUserInfo as _getUserInfo } from '@/api/user';//api
import useAppStore from '@/store/modules/app';
const { VITE_TOKEN_KEY } = import.meta.env;
// const token = useCookies().get(VITE_TOKEN_KEY as string);
let refreshTokenPromise;
let logOutPromise: Promise<void> | null = null;
interface StoreUser {
info: Record<string, any>; // 或者你可以更精确地定义 info 的结构
token: string | null;
id: string;
name: string;
avatar: string;
roles: string[];
permissions: string[];
}
export const useUserStore = defineStore({
id: 'app-user',
state: (): StoreUser => ({
info: {},
token: getToken(VITE_TOKEN_KEY),
id: '',
name: '',
avatar: '',
roles: [],
permissions: [],
}),
getters: {},
actions: {
setInfo(info: any) {
this.info = info ? info : '';
},
// 登录
login(userInfo) {
const username = userInfo.username.trim();
const password = userInfo.password;
const code = userInfo.code;
const uuid = userInfo.uuid;
return new Promise((resolve, reject) => {
login({ username, password, code, uuid })
.then((res) => {
setToken('accessToken', res.data.accessToken);
setToken('refreshToken', res.data.refreshToken);
resolve(res);
})
.catch((error) => {
reject(error);
});
});
},
//企业微信登录
loginWxWork(code) {
return new Promise((resolve, reject) => {
loginWxWork(code)
.then((res) => {
setToken('accessToken', res.data.accessToken);
setToken('refreshToken', res.data.refreshToken);
resolve(res);
})
.catch((err) => {
reject(err);
});
});
},
// 退出系统
logOut(): Promise<void> {
console.log('come?');
if (logOutPromise) {
console.log('logOutPromise->', logOutPromise);
return logOutPromise;
}
logOutPromise = new Promise<void>((resolve, reject) => {
logout()
.then(() => {
resolve();
})
.catch((err: unknown) => {
console.warn('logout err->', err);
reject(err);
})
.finally(() => {
this.token = '';
this.roles = [];
this.permissions = [];
removeToken('accessToken');
removeToken('refreshToken');
useAppStore().toggleRefreshStatus(true);
router.push({ path: '/login' });
});
}).finally(() => {
logOutPromise = null;
});
return logOutPromise;
},
// 获取用户信息
getUserInfo() {
return new Promise((resolve, reject) => {
_getUserInfo()
.then((res) => {
const user = res.data.user;
const avatar = user.avatar == '' || user.avatar == null ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
this.roles = res.data.roles;
this.permissions = res.data.permissions;
this.id = user.empLoginid;
this.name = user.empName;
this.avatar = avatar;
resolve(res.data);
})
.catch((err) => {
reject(err);
});
});
},
async refreshToken() {
if (refreshTokenPromise) {//这一步很重要,可以代替 Queen的创建 实现 未发送完的请求再拿到新token 之后重新拉起
return refreshTokenPromise;
}
refreshTokenPromise = new Promise(async (resolve, reject) => {
let res;
try {
res = await refreshToken();
if (res.code === 0) {
setToken('accessToken', res.data.accessToken);
setToken('refreshToken', res.data.refreshToken);
console.log('token refreshed at ', new Date());
}
resolve(res.code === 0);
} catch (err) {
reject(err);
}
})
.catch((_err) => {
return false;
})
.finally(() => {
refreshTokenPromise = null;
});
return refreshTokenPromise;
},
},
// persist: {
// key: 'token',
// storage: localStorage,
// paths: ['token'],
// },
});
公共状态库import useAppStore from '@/store/modules/app';
// import Cookies from 'js-cookie';
const useAppStore = defineStore('app', {
state: () => ({
// sidebar: {
// // opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
// withoutAnimation: false,
// hide: false,
// },
device: 'desktop',
// size: Cookies.get('size') || 'default',
isRefresh: true, //页面是否刷新
}),
actions: {
// toggleSideBar(withoutAnimation) {
// if (this.sidebar.hide) {
// return false;
// }
// this.sidebar.opened = !this.sidebar.opened;
// this.sidebar.withoutAnimation = withoutAnimation;
// if (this.sidebar.opened) {
// Cookies.set('sidebarStatus', 1);
// } else {
// Cookies.set('sidebarStatus', 0);
// }
// },
// closeSideBar({ withoutAnimation }) {
// Cookies.set('sidebarStatus', 0);
// this.sidebar.opened = false;
// this.sidebar.withoutAnimation = withoutAnimation;
// },
// toggleDevice(device) {
// this.device = device;
// },
// setSize(size) {
// this.size = size;
// Cookies.set('size', size);
// },
// toggleSideBarHide(status) {
// this.sidebar.hide = status;
// },
//切换刷新状态
toggleRefreshStatus(status) {
this.isRefresh = status;
},
},
});
export default useAppStore;