|
|
@@ -33,6 +33,90 @@ const processQueue = (error: any, token: string | null = null) => {
|
|
|
failedQueue = [];
|
|
|
};
|
|
|
|
|
|
+// 统一的token刷新函数
|
|
|
+const refreshTokenAndRetry = async (originalRequest: InternalAxiosRequestConfig & { _retry?: boolean }) => {
|
|
|
+ // 如果是刷新token的请求失败,不退出登录,继续保留当前页面
|
|
|
+ if (originalRequest.url?.includes('/refresh-token') || originalRequest._retry) {
|
|
|
+ const errorMessage = 'Token刷新失败,请稍后重试';
|
|
|
+ return Promise.reject(new Error(errorMessage));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果正在刷新token,将请求加入队列
|
|
|
+ if (isRefreshing) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ failedQueue.push({ resolve, reject });
|
|
|
+ })
|
|
|
+ .then((token) => {
|
|
|
+ if (originalRequest.headers) {
|
|
|
+ originalRequest.headers.Authorization = `Bearer ${token}`;
|
|
|
+ }
|
|
|
+ return axiosInstance(originalRequest);
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ return Promise.reject(err);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 尝试刷新token
|
|
|
+ const refreshToken = getRefreshToken();
|
|
|
+ if (!refreshToken) {
|
|
|
+ const errorMessage = 'RefreshToken不存在,无法刷新';
|
|
|
+ return Promise.reject(new Error(errorMessage));
|
|
|
+ }
|
|
|
+
|
|
|
+ isRefreshing = true;
|
|
|
+ originalRequest._retry = true;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const res: any = await loginApi.refreshToken(refreshToken);
|
|
|
+
|
|
|
+ // 处理返回数据格式:{code: 0, data: {accessToken, refreshToken, ...}, msg: ""}
|
|
|
+ let tokenData;
|
|
|
+ if (res?.code === 0 && res?.data) {
|
|
|
+ // 标准格式:{code: 0, data: {...}}
|
|
|
+ tokenData = res.data;
|
|
|
+ } else if (res?.data) {
|
|
|
+ // 兼容格式:{data: {...}}
|
|
|
+ tokenData = res.data;
|
|
|
+ } else {
|
|
|
+ // 直接是数据对象
|
|
|
+ tokenData = res;
|
|
|
+ }
|
|
|
+
|
|
|
+ const newAccessToken = tokenData?.accessToken || tokenData?.token;
|
|
|
+ const newRefreshToken = tokenData?.refreshToken;
|
|
|
+
|
|
|
+ if (newAccessToken) {
|
|
|
+ // 更新token
|
|
|
+ setToken({ accessToken: newAccessToken, token: newAccessToken });
|
|
|
+ setAccessToken(newAccessToken);
|
|
|
+ if (newRefreshToken) {
|
|
|
+ setRefreshToken(newRefreshToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新请求头
|
|
|
+ if (originalRequest.headers) {
|
|
|
+ originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理队列中的请求
|
|
|
+ processQueue(null, newAccessToken);
|
|
|
+
|
|
|
+ // 重试原始请求
|
|
|
+ return axiosInstance(originalRequest);
|
|
|
+ } else {
|
|
|
+ throw new Error('刷新token失败:未返回新token');
|
|
|
+ }
|
|
|
+ } catch (refreshError: any) {
|
|
|
+ // 刷新失败,不清除认证信息,不跳转登录,继续保留当前页面
|
|
|
+ const errorMessage = refreshError?.message || 'Token刷新失败,请稍后重试';
|
|
|
+ processQueue(refreshError, null);
|
|
|
+ return Promise.reject(new Error(errorMessage));
|
|
|
+ } finally {
|
|
|
+ isRefreshing = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
// 请求拦截器
|
|
|
axiosInstance.interceptors.request.use(
|
|
|
(config) => {
|
|
|
@@ -76,91 +160,7 @@ axiosInstance.interceptors.response.use(
|
|
|
} else if (data.code === 401) {
|
|
|
// 业务错误码 401,表示未登录,需要处理 token 刷新或退出登录
|
|
|
const originalRequest = config as InternalAxiosRequestConfig & { _retry?: boolean };
|
|
|
-
|
|
|
- // 如果是刷新token的请求失败,不退出登录,继续保留当前页面
|
|
|
- if (originalRequest.url?.includes('/refresh-token') || originalRequest._retry) {
|
|
|
- const errorMessage = 'Token刷新失败,请稍后重试';
|
|
|
- // 不清除认证信息,不跳转登录,继续保留当前页面
|
|
|
- return Promise.reject(new Error(errorMessage));
|
|
|
- }
|
|
|
-
|
|
|
- // 如果正在刷新token,将请求加入队列
|
|
|
- if (isRefreshing) {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- failedQueue.push({ resolve, reject });
|
|
|
- })
|
|
|
- .then((token) => {
|
|
|
- if (originalRequest.headers) {
|
|
|
- originalRequest.headers.Authorization = `Bearer ${token}`;
|
|
|
- }
|
|
|
- return axiosInstance(originalRequest);
|
|
|
- })
|
|
|
- .catch((err) => {
|
|
|
- return Promise.reject(err);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 尝试刷新token
|
|
|
- const refreshToken = getRefreshToken();
|
|
|
- if (!refreshToken) {
|
|
|
- const errorMessage = 'RefreshToken不存在,无法刷新';
|
|
|
- // 不清除认证信息,不跳转登录,继续保留当前页面
|
|
|
- return Promise.reject(new Error(errorMessage));
|
|
|
- }
|
|
|
-
|
|
|
- isRefreshing = true;
|
|
|
- originalRequest._retry = true;
|
|
|
-
|
|
|
- return loginApi.refreshToken(refreshToken)
|
|
|
- .then((res: any) => {
|
|
|
- // 处理返回数据格式:{code: 0, data: {accessToken, refreshToken, ...}, msg: ""}
|
|
|
- let tokenData;
|
|
|
- if (res?.code === 0 && res?.data) {
|
|
|
- // 标准格式:{code: 0, data: {...}}
|
|
|
- tokenData = res.data;
|
|
|
- } else if (res?.data) {
|
|
|
- // 兼容格式:{data: {...}}
|
|
|
- tokenData = res.data;
|
|
|
- } else {
|
|
|
- // 直接是数据对象
|
|
|
- tokenData = res;
|
|
|
- }
|
|
|
-
|
|
|
- const newAccessToken = tokenData?.accessToken || tokenData?.token;
|
|
|
- const newRefreshToken = tokenData?.refreshToken;
|
|
|
-
|
|
|
- if (newAccessToken) {
|
|
|
- // 更新token
|
|
|
- setToken({ accessToken: newAccessToken, token: newAccessToken });
|
|
|
- setAccessToken(newAccessToken);
|
|
|
- if (newRefreshToken) {
|
|
|
- setRefreshToken(newRefreshToken);
|
|
|
- }
|
|
|
-
|
|
|
- // 更新请求头
|
|
|
- if (originalRequest.headers) {
|
|
|
- originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
|
|
|
- }
|
|
|
-
|
|
|
- // 处理队列中的请求
|
|
|
- processQueue(null, newAccessToken);
|
|
|
-
|
|
|
- // 重试原始请求
|
|
|
- return axiosInstance(originalRequest);
|
|
|
- } else {
|
|
|
- throw new Error('刷新token失败:未返回新token');
|
|
|
- }
|
|
|
- })
|
|
|
- .catch((refreshError: any) => {
|
|
|
- // 刷新失败,不清除认证信息,不跳转登录,继续保留当前页面
|
|
|
- const errorMessage = refreshError?.message || 'Token刷新失败,请稍后重试';
|
|
|
- processQueue(refreshError, null);
|
|
|
- // 不清除认证信息,不跳转登录
|
|
|
- return Promise.reject(new Error(errorMessage));
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- isRefreshing = false;
|
|
|
- });
|
|
|
+ return refreshTokenAndRetry(originalRequest);
|
|
|
} else {
|
|
|
// 其他业务错误 - 创建一个包含更多信息的错误对象
|
|
|
const errorMessage = data.message || data.msg || '请求失败';
|
|
|
@@ -174,88 +174,7 @@ axiosInstance.interceptors.response.use(
|
|
|
// 如果 code 是 401,需要处理认证问题
|
|
|
if (data.code === 401) {
|
|
|
const originalRequest = config as InternalAxiosRequestConfig & { _retry?: boolean };
|
|
|
-
|
|
|
- // 如果是刷新token的请求失败,不退出登录,继续保留当前页面
|
|
|
- if (originalRequest.url?.includes('/refresh-token') || originalRequest._retry) {
|
|
|
- // 不清除认证信息,不跳转登录,继续保留当前页面
|
|
|
- return Promise.reject(error);
|
|
|
- }
|
|
|
-
|
|
|
- // 如果正在刷新token,将请求加入队列
|
|
|
- if (isRefreshing) {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- failedQueue.push({ resolve, reject });
|
|
|
- })
|
|
|
- .then((token) => {
|
|
|
- if (originalRequest.headers) {
|
|
|
- originalRequest.headers.Authorization = `Bearer ${token}`;
|
|
|
- }
|
|
|
- return axiosInstance(originalRequest);
|
|
|
- })
|
|
|
- .catch((err) => {
|
|
|
- return Promise.reject(err);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 尝试刷新token
|
|
|
- const refreshToken = getRefreshToken();
|
|
|
- if (!refreshToken) {
|
|
|
- // 不清除认证信息,不跳转登录,继续保留当前页面
|
|
|
- return Promise.reject(error);
|
|
|
- }
|
|
|
-
|
|
|
- isRefreshing = true;
|
|
|
- originalRequest._retry = true;
|
|
|
-
|
|
|
- return loginApi.refreshToken(refreshToken)
|
|
|
- .then((res: any) => {
|
|
|
- // 处理返回数据格式:{code: 0, data: {accessToken, refreshToken, ...}, msg: ""}
|
|
|
- let tokenData;
|
|
|
- if (res?.code === 0 && res?.data) {
|
|
|
- // 标准格式:{code: 0, data: {...}}
|
|
|
- tokenData = res.data;
|
|
|
- } else if (res?.data) {
|
|
|
- // 兼容格式:{data: {...}}
|
|
|
- tokenData = res.data;
|
|
|
- } else {
|
|
|
- // 直接是数据对象
|
|
|
- tokenData = res;
|
|
|
- }
|
|
|
-
|
|
|
- const newAccessToken = tokenData?.accessToken || tokenData?.token;
|
|
|
- const newRefreshToken = tokenData?.refreshToken;
|
|
|
-
|
|
|
- if (newAccessToken) {
|
|
|
- // 更新token
|
|
|
- setToken({ accessToken: newAccessToken, token: newAccessToken });
|
|
|
- setAccessToken(newAccessToken);
|
|
|
- if (newRefreshToken) {
|
|
|
- setRefreshToken(newRefreshToken);
|
|
|
- }
|
|
|
-
|
|
|
- // 更新请求头
|
|
|
- if (originalRequest.headers) {
|
|
|
- originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
|
|
|
- }
|
|
|
-
|
|
|
- // 处理队列中的请求
|
|
|
- processQueue(null, newAccessToken);
|
|
|
-
|
|
|
- // 重试原始请求
|
|
|
- return axiosInstance(originalRequest);
|
|
|
- } else {
|
|
|
- throw new Error('刷新token失败:未返回新token');
|
|
|
- }
|
|
|
- })
|
|
|
- .catch((refreshError: any) => {
|
|
|
- // 刷新失败,不清除认证信息,不跳转登录,继续保留当前页面
|
|
|
- processQueue(refreshError, null);
|
|
|
- // 不清除认证信息,不跳转登录
|
|
|
- return Promise.reject(error);
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- isRefreshing = false;
|
|
|
- });
|
|
|
+ return refreshTokenAndRetry(originalRequest);
|
|
|
}
|
|
|
|
|
|
// 不在这里显示 toast,让调用方决定是否显示
|
|
|
@@ -277,91 +196,10 @@ axiosInstance.interceptors.response.use(
|
|
|
case 401:
|
|
|
// 获取原始请求配置
|
|
|
const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean };
|
|
|
-
|
|
|
- // 如果是刷新token的请求失败,不退出登录,继续保留当前页面
|
|
|
- if (originalRequest.url?.includes('/refresh-token') || originalRequest._retry) {
|
|
|
- errorMessage = 'Token刷新失败,请稍后重试';
|
|
|
- // 不清除认证信息,不跳转登录,继续保留当前页面
|
|
|
- return Promise.reject(new Error(errorMessage));
|
|
|
- }
|
|
|
-
|
|
|
- // 如果正在刷新token,将请求加入队列
|
|
|
- if (isRefreshing) {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- failedQueue.push({ resolve, reject });
|
|
|
- })
|
|
|
- .then((token) => {
|
|
|
- if (originalRequest.headers) {
|
|
|
- originalRequest.headers.Authorization = `Bearer ${token}`;
|
|
|
- }
|
|
|
- return axiosInstance(originalRequest);
|
|
|
- })
|
|
|
- .catch((err) => {
|
|
|
- return Promise.reject(err);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 尝试刷新token
|
|
|
- const refreshToken = getRefreshToken();
|
|
|
- if (!refreshToken) {
|
|
|
- errorMessage = 'RefreshToken不存在,无法刷新';
|
|
|
- // 不清除认证信息,不跳转登录,继续保留当前页面
|
|
|
- return Promise.reject(new Error(errorMessage));
|
|
|
+ if (originalRequest) {
|
|
|
+ return refreshTokenAndRetry(originalRequest);
|
|
|
}
|
|
|
-
|
|
|
- isRefreshing = true;
|
|
|
- originalRequest._retry = true;
|
|
|
-
|
|
|
- return loginApi.refreshToken(refreshToken)
|
|
|
- .then((res: any) => {
|
|
|
- // 处理返回数据格式:{code: 0, data: {accessToken, refreshToken, ...}, msg: ""}
|
|
|
- let tokenData;
|
|
|
- if (res?.code === 0 && res?.data) {
|
|
|
- // 标准格式:{code: 0, data: {...}}
|
|
|
- tokenData = res.data;
|
|
|
- } else if (res?.data) {
|
|
|
- // 兼容格式:{data: {...}}
|
|
|
- tokenData = res.data;
|
|
|
- } else {
|
|
|
- // 直接是数据对象
|
|
|
- tokenData = res;
|
|
|
- }
|
|
|
-
|
|
|
- const newAccessToken = tokenData?.accessToken || tokenData?.token;
|
|
|
- const newRefreshToken = tokenData?.refreshToken;
|
|
|
-
|
|
|
- if (newAccessToken) {
|
|
|
- // 更新token
|
|
|
- setToken({ accessToken: newAccessToken, token: newAccessToken });
|
|
|
- setAccessToken(newAccessToken);
|
|
|
- if (newRefreshToken) {
|
|
|
- setRefreshToken(newRefreshToken);
|
|
|
- }
|
|
|
-
|
|
|
- // 更新请求头
|
|
|
- if (originalRequest.headers) {
|
|
|
- originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
|
|
|
- }
|
|
|
-
|
|
|
- // 处理队列中的请求
|
|
|
- processQueue(null, newAccessToken);
|
|
|
-
|
|
|
- // 重试原始请求
|
|
|
- return axiosInstance(originalRequest);
|
|
|
- } else {
|
|
|
- throw new Error('刷新token失败:未返回新token');
|
|
|
- }
|
|
|
- })
|
|
|
- .catch((refreshError: any) => {
|
|
|
- // 刷新失败,不清除认证信息,不跳转登录,继续保留当前页面
|
|
|
- errorMessage = refreshError?.message || 'Token刷新失败,请稍后重试';
|
|
|
- processQueue(refreshError, null);
|
|
|
- // 不清除认证信息,不跳转登录
|
|
|
- return Promise.reject(new Error(errorMessage));
|
|
|
- })
|
|
|
- .finally(() => {
|
|
|
- isRefreshing = false;
|
|
|
- });
|
|
|
+ errorMessage = '未授权,请重新登录';
|
|
|
break;
|
|
|
case 403:
|
|
|
errorMessage = '拒绝访问';
|