From 894d8d7dfe77c30c8ec35c379a3a13378d8e5a6c Mon Sep 17 00:00:00 2001 From: colin Date: Sat, 9 Aug 2025 10:46:46 +0800 Subject: [PATCH] feat(vben5): user logout - add a token revocation api - the token is revoked when the user logs out - add an logout button to the 404 page --- apps/vben5/apps/app-antd/src/store/auth.ts | 21 ++++++++- .../src/views/_core/fallback/not-found.vue | 46 ++++++++++++++++++- .../@abp/account/src/api/useTokenApi.ts | 25 ++++++++++ .../packages/@abp/account/src/types/token.ts | 8 ++++ 4 files changed, 97 insertions(+), 3 deletions(-) diff --git a/apps/vben5/apps/app-antd/src/store/auth.ts b/apps/vben5/apps/app-antd/src/store/auth.ts index 7492529a5..c44b207ca 100644 --- a/apps/vben5/apps/app-antd/src/store/auth.ts +++ b/apps/vben5/apps/app-antd/src/store/auth.ts @@ -26,7 +26,7 @@ import { $t } from '#/locales'; export const useAuthStore = defineStore('auth', () => { const { publish } = useEventBus(); - const { loginApi, refreshTokenApi } = useTokenApi(); + const { loginApi, refreshTokenApi, logoutApi } = useTokenApi(); const { loginApi: qrcodeLoginApi } = useQrCodeLoginApi(); const { loginApi: phoneLoginApi } = usePhoneLoginApi(); const { getUserInfoApi } = useUserInfoApi(); @@ -131,6 +131,25 @@ export const useAuthStore = defineStore('auth', () => { if (await oidcClient.getAccessToken()) { accessStore.setAccessToken(null); await oidcClient.logout(); + } else { + const logoutTasks: Promise[] = []; + if (accessStore.accessToken) { + logoutTasks.push( + logoutApi({ + token: accessStore.accessToken, + tokenType: 'access_token', + }), + ); + } + if (accessStore.refreshToken) { + logoutTasks.push( + logoutApi({ + token: accessStore.refreshToken, + tokenType: 'refresh_token', + }), + ); + } + await Promise.all(logoutTasks); } } catch { // 不做任何处理 diff --git a/apps/vben5/apps/app-antd/src/views/_core/fallback/not-found.vue b/apps/vben5/apps/app-antd/src/views/_core/fallback/not-found.vue index 4d178e9cb..9fa96d7f1 100644 --- a/apps/vben5/apps/app-antd/src/views/_core/fallback/not-found.vue +++ b/apps/vben5/apps/app-antd/src/views/_core/fallback/not-found.vue @@ -1,9 +1,51 @@ diff --git a/apps/vben5/packages/@abp/account/src/api/useTokenApi.ts b/apps/vben5/packages/@abp/account/src/api/useTokenApi.ts index e41e84c10..1f8c4ac2f 100644 --- a/apps/vben5/packages/@abp/account/src/api/useTokenApi.ts +++ b/apps/vben5/packages/@abp/account/src/api/useTokenApi.ts @@ -2,6 +2,7 @@ import type { OAuthTokenRefreshModel, OAuthTokenResult, PasswordTokenRequestModel, + RevokeTokenRequest, TokenResult, } from '../types'; @@ -75,9 +76,33 @@ export function useTokenApi() { }; } + /** + * 注销登录 + * @param input 参数 + */ + async function logoutApi(input: RevokeTokenRequest): Promise { + const { clientId, clientSecret } = useAppConfig( + import.meta.env, + import.meta.env.PROD, + ); + return await request('/connect/revocat', { + data: { + client_id: clientId, + client_secret: clientSecret, + token: input.token, + token_type_hint: input.tokenType, + }, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + method: 'POST', + }); + } + return { cancel, loginApi, + logoutApi, refreshTokenApi, }; } diff --git a/apps/vben5/packages/@abp/account/src/types/token.ts b/apps/vben5/packages/@abp/account/src/types/token.ts index 78df62c18..c29967c6f 100644 --- a/apps/vben5/packages/@abp/account/src/types/token.ts +++ b/apps/vben5/packages/@abp/account/src/types/token.ts @@ -28,6 +28,13 @@ interface QrCodeTokenRequest { /** 租户Id */ tenantId?: string; } +/** 令牌撤销请求数据类型 */ +interface RevokeTokenRequest { + /** 令牌 */ + token: string; + /** 令牌类型 */ + tokenType?: 'access_token' | 'refresh_token'; +} /** 用户密码授权请求数据模型 */ interface PasswordTokenRequestModel { /** 用户密码 */ @@ -89,6 +96,7 @@ export type { PasswordTokenRequestModel, PhoneNumberTokenRequest, QrCodeTokenRequest, + RevokeTokenRequest, ShouldChangePasswordError, TokenRequest, TokenResult,