diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b59365a..28dedc14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - websocket功能(默认关闭) - useVbenForm 增加 TimeRangePicker(时间区间选择) 组件 - 字典(DictTag)支持fallback属性(未匹配到字典项时的回显) +- 微服务版本 logout接口在配置错误的情况返回401的提示(解决死循环调用logout接口) **REFACTOR** diff --git a/apps/web-antd/src/api/core/auth.ts b/apps/web-antd/src/api/core/auth.ts index e3a99ef2..173d3435 100644 --- a/apps/web-antd/src/api/core/auth.ts +++ b/apps/web-antd/src/api/core/auth.ts @@ -1,12 +1,8 @@ import type { GrantType } from '@vben/common-ui'; import type { HttpResponse } from '@vben/request'; -import { h } from 'vue'; - import { useAppConfig } from '@vben/hooks'; -import { Modal } from 'ant-design-vue'; - import { requestClient } from '#/api/request'; const { clientId, sseEnable } = useAppConfig( @@ -95,32 +91,8 @@ export async function loginApi(data: AuthApi.LoginParams) { * 用户登出 * @returns void */ -export async function doLogout() { - const resp = await requestClient.post>( - '/auth/logout', - null, - { - isTransformResponse: false, - }, - ); - // 无奈之举 对错误用法的提示 - if (resp.code === 401 && import.meta.env.DEV) { - Modal.destroyAll(); - Modal.warn({ - title: '后端配置出现错误', - centered: true, - content: h('div', { class: 'flex flex-col gap-2' }, [ - `检测到你的logout接口返回了401, 导致前端一直进入循环逻辑???`, - ...Array.from({ length: 3 }, () => - h( - 'span', - { class: 'font-bold text-red-500 text-[18px]' }, - '去检查你的后端配置!别盯着前端找问题了!这不是前端问题!', - ), - ), - ]), - }); - } +export function doLogout() { + return requestClient.post>('/auth/logout'); } /** diff --git a/apps/web-antd/src/api/helper.ts b/apps/web-antd/src/api/helper.ts index 78246f9d..55174d1c 100644 --- a/apps/web-antd/src/api/helper.ts +++ b/apps/web-antd/src/api/helper.ts @@ -1,6 +1,6 @@ import { $t } from '@vben/locales'; -import { message } from 'ant-design-vue'; +import { message, Modal } from 'ant-design-vue'; import { useAuthStore } from '#/store'; @@ -38,11 +38,20 @@ export function commonExport(url: string, data: Record) { */ export class UnauthorizedException extends Error {} +/** + * logout这种接口都返回401 抛出这个异常 + */ +export class ImpossibleReturn401Exception extends Error {} + /** * 是否已经处在登出过程中了 一个标志位 * 主要是防止一个页面会请求多个api 都401 会导致登出执行多次 */ let isLogoutProcessing = false; +/** + * 防止 调用logout接口 logout又返回401 然后又走到Logout逻辑死循环 + */ +let lockLogoutRequest = false; /** * 登出逻辑 两个地方用到 提取出来 @@ -50,17 +59,43 @@ let isLogoutProcessing = false; */ export function handleUnauthorizedLogout() { const timeoutMsg = $t('http.loginTimeout'); - + /** + * lock 不再请求logout接口 + * 这里已经算异常情况了 + */ + if (lockLogoutRequest) { + throw new UnauthorizedException(timeoutMsg); + } // 已经在登出过程中 不再执行 if (isLogoutProcessing) { throw new UnauthorizedException(timeoutMsg); } isLogoutProcessing = true; const userStore = useAuthStore(); - userStore.logout().finally(() => { - message.error(timeoutMsg); - isLogoutProcessing = false; - }); + userStore + .logout() + .catch((error) => { + /** + * logout接口返回了401 + * 做Lock处理 且 该标志位不会复位(因为这种场景出现 系统已经算故障了) + * 因为这已经不符合正常的逻辑了 + */ + if (error instanceof ImpossibleReturn401Exception) { + lockLogoutRequest = true; + if (import.meta.env.DEV) { + Modal.error({ + title: '提示', + centered: true, + content: + '检测到你的logout接口返回了401, 去检查你的后端配置 这已经不符合正常逻辑(该提示不会在非dev环境弹出)', + }); + } + } + }) + .finally(() => { + message.error(timeoutMsg); + isLogoutProcessing = false; + }); // 不再执行下面逻辑 throw new UnauthorizedException(timeoutMsg); } diff --git a/apps/web-antd/src/store/auth.ts b/apps/web-antd/src/store/auth.ts index 30ae1f4a..2f2a5da3 100644 --- a/apps/web-antd/src/store/auth.ts +++ b/apps/web-antd/src/store/auth.ts @@ -12,6 +12,10 @@ import { notification } from 'ant-design-vue'; import { defineStore } from 'pinia'; import { doLogout, getUserInfoApi, loginApi, seeConnectionClose } from '#/api'; +import { + ImpossibleReturn401Exception, + UnauthorizedException, +} from '#/api/helper'; import { $t } from '#/locales'; import { useDictStore } from './dict'; @@ -83,6 +87,14 @@ export const useAuthStore = defineStore('auth', () => { await Promise.all([seeConnectionClose(), doLogout()]); } catch (error) { console.error(error); + /** + * 这两个接口按正常逻辑不可能返回401 + * 在微服务版本配置错误的情况下 这里会抛出401 + * 在这里抛出自定义异常供上层处理 + */ + if (error instanceof UnauthorizedException) { + throw new ImpossibleReturn401Exception(error.message); + } } finally { resetAllStores(); accessStore.setLoginExpired(false);