Skip to content

Commit

Permalink
chore: refresh token逻辑调整
Browse files Browse the repository at this point in the history
  • Loading branch information
vince292007 committed Aug 19, 2024
1 parent 081a2e8 commit 519e2f4
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 51 deletions.
4 changes: 4 additions & 0 deletions packages/effects/request/src/request-client/request-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ class RequestClient {
public addResponseInterceptor: InterceptorManager['addResponseInterceptor'];

public download: FileDownloader['download'];
// 是否正在刷新token
public isRefreshing = false;
// 刷新token队列
public refreshTokenQueue: ((token: string) => void)[] = [];
public upload: FileUploader['upload'];

/**
Expand Down
119 changes: 68 additions & 51 deletions playground/src/api/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,29 @@ import { useAccessStore } from '@vben/stores';

import { message } from 'ant-design-vue';

import { refreshTokenApi } from '#/api/core';
import { useAuthStore } from '#/store';

import { refreshTokenApi } from './core';

const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);

function createRequestClient(baseURL: string) {
const client = new RequestClient({
baseURL,
});

function toReAuthenticate() {
console.warn('Access token or refresh token is invalid or expired. ');
const accessStore = useAccessStore();
const authStore = useAuthStore();
accessStore.setAccessToken(null);
if (preferences.app.loginExpiredMode === 'modal') {
accessStore.setLoginExpired(true);
} else {
authStore.logout();
}
}

client.addRequestInterceptor({
fulfilled: (config) => {
const accessStore = useAccessStore();
Expand All @@ -41,61 +54,65 @@ function createRequestClient(baseURL: string) {
},
});

if (preferences.app.refreshToken) {
client.addResponseInterceptor({
rejected: async (error) => {
// refreshToken无效的处理方式
if (error.response.status === 403) {
const accessStore = useAccessStore();
const authStore = useAuthStore();
accessStore.setAccessToken(null);

if (preferences.app.loginExpiredMode === 'modal') {
accessStore.setLoginExpired(true);
} else {
await authStore.logout();
}
}

// refreshToken的处理方式
if (error.response.status === 401) {
const prevRequest = error.config;
const accessStore = useAccessStore();
if (!prevRequest?.sent) {
prevRequest.sent = true;
const resp = await refreshTokenApi();

accessStore.setAccessToken(resp.data);
return client.request(prevRequest.url, { ...prevRequest });
}
}
// 响应拦截器:处理 401 错误和刷新 token 逻辑
client.addResponseInterceptor({
rejected: async (error) => {
const { config, response } = error;
// 如果不是 401 错误,直接抛出异常
if (response?.status !== 401) {
throw error;
},
});
} else {
client.addResponseInterceptor({
rejected: async (error) => {
if (error.response.status === 401) {
const accessStore = useAccessStore();
const authStore = useAuthStore();
accessStore.setAccessToken(null);

if (preferences.app.loginExpiredMode === 'modal') {
accessStore.setLoginExpired(true);
} else {
await authStore.logout();
}
}
}
// 判断是否启用了 refreshToken 功能
// 如果没有启用或者已经是重试请求了,直接跳转到重新登录
if (!preferences.app.refreshToken || config.__isRetryRequest) {
toReAuthenticate();
throw error;
},
});
}
}
// 如果正在刷新 token,则将请求加入队列,等待刷新完成
if (client.isRefreshing) {
return new Promise((resolve) => {
client.refreshTokenQueue.push((newToken: string) => {
config.headers.Authorization = `Bearer ${newToken}`;
resolve(client.request(config.url, { ...config }));
});
});
}

client.addResponseInterceptor({
rejected: () =>
errorMessageResponseInterceptor((msg: any) => message.error(msg)),
// 标记开始刷新 token
client.isRefreshing = true;
// 标记当前请求为重试请求,避免无限循环
config.__isRetryRequest = true;

try {
const accessStore = useAccessStore();
const resp = await refreshTokenApi();
const newToken = resp.data;
accessStore.setAccessToken(newToken);

// 处理队列中的请求
client.refreshTokenQueue.forEach((callback) => callback(newToken));
// 清空队列
client.refreshTokenQueue = [];

return client.request(error.config.url, { ...error.config });
} catch (refreshError) {
// 如果刷新 token 失败,处理错误(如强制登出或跳转登录页面)
client.refreshTokenQueue.forEach((callback) => callback(''));
client.refreshTokenQueue = [];
console.error('Refresh token failed, please login again.');
throw refreshError;
} finally {
client.isRefreshing = false;
}
},
});

// 通用的错误处理
// 如果没有进入上面的错误处理逻辑,就会进入这里
client.addResponseInterceptor(
errorMessageResponseInterceptor((msg: any) => message.error(msg)),
);

return client;
}

Expand Down

0 comments on commit 519e2f4

Please sign in to comment.