From db51fc5fbad6636d50bc5856058442893f851174 Mon Sep 17 00:00:00 2001 From: Dallas98 <990259227@qq.com> Date: Mon, 23 Mar 2026 10:47:44 +0800 Subject: [PATCH 1/6] feat: add AuthGuard component for enhanced route protection --- frontend/src/main.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index f1d826e6..d01d1321 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -5,6 +5,7 @@ import router from "./routes/routes"; import { App as AntdApp, Spin, ConfigProvider } from "antd"; import "./index.css"; import TopLoadingBar from "./components/TopLoadingBar"; +import AuthGuard from "./components/AuthGuard"; import { store } from "./store"; import { Provider } from "react-redux"; import theme from "./theme"; @@ -94,6 +95,7 @@ async function bootstrap() { }> + From 2d843b3fcb801b45e74cd376e51269c47af1d04d Mon Sep 17 00:00:00 2001 From: Dallas98 <990259227@qq.com> Date: Mon, 23 Mar 2026 10:52:29 +0800 Subject: [PATCH 2/6] feat: add AuthGuard component for enhanced route protection --- frontend/src/components/AuthGuard.tsx | 101 ++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 frontend/src/components/AuthGuard.tsx diff --git a/frontend/src/components/AuthGuard.tsx b/frontend/src/components/AuthGuard.tsx new file mode 100644 index 00000000..e7963057 --- /dev/null +++ b/frontend/src/components/AuthGuard.tsx @@ -0,0 +1,101 @@ +import { useState, useEffect, useCallback } from "react"; +import { message } from "antd"; +import { LoginDialog } from "@/pages/Layout/LoginDialog"; +import { SignupDialog } from "@/pages/Layout/SignupDialog"; +import { post } from "@/utils/request"; +import { useTranslation } from "react-i18next"; + +function loginUsingPost(data: { username: string; password: string }) { + return post("/api/user/login", data); +} + +function signupUsingPost(data: { username: string; email: string; password: string }) { + return post("/api/user/signup", data); +} + +export function AuthGuard() { + const { t } = useTranslation(); + const [loginOpen, setLoginOpen] = useState(false); + const [signupOpen, setSignupOpen] = useState(false); + const [loading, setLoading] = useState(false); + + const openLoginDialog = useCallback(() => { + setLoginOpen(true); + }, []); + + const openSignupDialog = useCallback(() => { + setSignupOpen(true); + }, []); + + useEffect(() => { + window.addEventListener("show-login", openLoginDialog); + + return () => { + window.removeEventListener("show-login", openLoginDialog); + }; + }, [openLoginDialog]); + + const handleLogin = async (values: { username: string; password: string }) => { + try { + setLoading(true); + const response = await loginUsingPost(values); + localStorage.setItem("session", JSON.stringify(response.data)); + message.success(t("user.messages.loginSuccess")); + setLoginOpen(false); + window.location.reload(); + } catch (error) { + console.error("Login error:", error); + message.error(t("user.messages.loginFailed")); + } finally { + setLoading(false); + } + }; + + const handleSignup = async (values: { + username: string; + email: string; + password: string; + confirmPassword: string; + }) => { + if (values.password !== values.confirmPassword) { + message.error(t("user.messages.passwordMismatch")); + return; + } + + try { + setLoading(true); + const { username, email, password } = values; + const response = await signupUsingPost({ username, email, password }); + message.success(t("user.messages.signupSuccess")); + localStorage.setItem("session", JSON.stringify(response.data)); + setSignupOpen(false); + window.location.reload(); + } catch (error) { + console.error("Registration error:", error); + message.error(t("user.messages.signupFailed")); + } finally { + setLoading(false); + } + }; + + return ( + <> + + + + ); +} + +export default AuthGuard; From 37ce4853452d15a477ab84e62cc66e27efad91fb Mon Sep 17 00:00:00 2001 From: Dallas98 <990259227@qq.com> Date: Mon, 23 Mar 2026 11:05:10 +0800 Subject: [PATCH 3/6] feat: add AuthGuard component for enhanced route protection --- frontend/src/components/AuthGuard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/AuthGuard.tsx b/frontend/src/components/AuthGuard.tsx index e7963057..4e4be9ff 100644 --- a/frontend/src/components/AuthGuard.tsx +++ b/frontend/src/components/AuthGuard.tsx @@ -89,7 +89,7 @@ export function AuthGuard() { /> Date: Mon, 23 Mar 2026 11:59:02 +0800 Subject: [PATCH 4/6] feat: add AuthGuard component for enhanced route protection --- frontend/src/components/AuthGuard.tsx | 10 ++++++++-- frontend/src/utils/request.ts | 4 +--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/AuthGuard.tsx b/frontend/src/components/AuthGuard.tsx index 4e4be9ff..efc6eba0 100644 --- a/frontend/src/components/AuthGuard.tsx +++ b/frontend/src/components/AuthGuard.tsx @@ -2,7 +2,7 @@ import { useState, useEffect, useCallback } from "react"; import { message } from "antd"; import { LoginDialog } from "@/pages/Layout/LoginDialog"; import { SignupDialog } from "@/pages/Layout/SignupDialog"; -import { post } from "@/utils/request"; +import { post, get } from "@/utils/request"; import { useTranslation } from "react-i18next"; function loginUsingPost(data: { username: string; password: string }) { @@ -29,12 +29,18 @@ export function AuthGuard() { useEffect(() => { window.addEventListener("show-login", openLoginDialog); - return () => { window.removeEventListener("show-login", openLoginDialog); }; }, [openLoginDialog]); + useEffect(() => { + const session = localStorage.getItem("session"); + if (!session) { + get("/api/sys-param/sys.home.page.url").catch(() => {}); + } + }, []); + const handleLogin = async (values: { username: string; password: string }) => { try { setLoading(true); diff --git a/frontend/src/utils/request.ts b/frontend/src/utils/request.ts index 62040176..52e5ef72 100644 --- a/frontend/src/utils/request.ts +++ b/frontend/src/utils/request.ts @@ -526,11 +526,9 @@ request.addRequestInterceptor((config) => { try { const sessionData = JSON.parse(session); if (sessionData.token) { - // 后端使用 "User" 请求头而不是 "Authorization" - // 可以直接发送 token 或 username config.headers = { ...config.headers, - 'User': sessionData.token, // 使用 User 请求头 + 'Authorization': `Bearer ${sessionData.token}`, }; } } catch (e) { From 3ba9ecbfa52bd75dd629ceb004e3b97247e7ca81 Mon Sep 17 00:00:00 2001 From: Dallas98 <990259227@qq.com> Date: Mon, 23 Mar 2026 15:15:12 +0800 Subject: [PATCH 5/6] feat: update login redirect logic to navigate directly to login page --- frontend/src/utils/request.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/utils/request.ts b/frontend/src/utils/request.ts index 52e5ef72..7043329f 100644 --- a/frontend/src/utils/request.ts +++ b/frontend/src/utils/request.ts @@ -552,6 +552,7 @@ const AUTH_ERR_CODES = [401, '401', 'common.401']; // --- 辅助函数:防抖处理登录失效 --- let isRelogging = false; + const handleLoginRedirect = () => { if (isRelogging) return; isRelogging = true; @@ -559,9 +560,8 @@ const handleLoginRedirect = () => { // 1. 清除 Session / Token localStorage.removeItem('session'); - // 2. 触发登录弹窗事件 (根据你的架构,这里可以是 dispatch event 或 router 跳转) - const loginEvent = new CustomEvent('show-login'); - window.dispatchEvent(loginEvent); + // 2. 跳转到登录页面 + window.location.href = '/login'; // 3. 重置标志位 (3秒后才允许再次触发) setTimeout(() => { From 18943a3118fbb6b69a02d2856dc500185656fe2a Mon Sep 17 00:00:00 2001 From: Dallas98 <990259227@qq.com> Date: Mon, 23 Mar 2026 16:03:08 +0800 Subject: [PATCH 6/6] feat: update login redirect logic to navigate directly to login page --- frontend/src/components/AuthGuard.tsx | 5 +++++ frontend/src/utils/request.ts | 29 +++++++++++++++++---------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/AuthGuard.tsx b/frontend/src/components/AuthGuard.tsx index efc6eba0..af548306 100644 --- a/frontend/src/components/AuthGuard.tsx +++ b/frontend/src/components/AuthGuard.tsx @@ -20,16 +20,21 @@ export function AuthGuard() { const [loading, setLoading] = useState(false); const openLoginDialog = useCallback(() => { + console.log('[AuthGuard] openLoginDialog called, setting loginOpen to true'); setLoginOpen(true); }, []); const openSignupDialog = useCallback(() => { + console.log('[AuthGuard] openSignupDialog called'); setSignupOpen(true); }, []); useEffect(() => { + console.log('[AuthGuard] Registering show-login event listener'); window.addEventListener("show-login", openLoginDialog); + return () => { + console.log('[AuthGuard] Removing show-login event listener'); window.removeEventListener("show-login", openLoginDialog); }; }, [openLoginDialog]); diff --git a/frontend/src/utils/request.ts b/frontend/src/utils/request.ts index 7043329f..ef92fef9 100644 --- a/frontend/src/utils/request.ts +++ b/frontend/src/utils/request.ts @@ -2,9 +2,6 @@ import {message} from "antd"; import Loading from "./loading"; import {errorConfigStore} from "@/utils/errorConfigStore.ts"; import i18n from "@/i18n"; -import i18n from "@/i18n"; -import i18n from "@/i18n"; -import i18n from "@/i18n"; /** * 通用请求工具类 @@ -548,24 +545,29 @@ request.addRequestInterceptor((config) => { // --- 常量配置 --- const DEFAULT_ERROR_MSG = '系统繁忙,请稍后重试'; // 需要触发重新登录的 Code 集合 (包含 HTTP 401 和 业务 Token 过期码) -const AUTH_ERR_CODES = [401, '401', 'common.401']; +// 注意:后端返回的是 "common.0401"(有前导零) +const AUTH_ERR_CODES = [401, '401', 'common.401', 'common.0401']; // --- 辅助函数:防抖处理登录失效 --- let isRelogging = false; const handleLoginRedirect = () => { - if (isRelogging) return; + console.log('[Auth] handleLoginRedirect called, isRelogging:', isRelogging); + + if (isRelogging) { + console.log('[Auth] Skipping - already relogging'); + return; + } isRelogging = true; - // 1. 清除 Session / Token localStorage.removeItem('session'); - // 2. 跳转到登录页面 - window.location.href = '/login'; + console.log('[Auth] Dispatching show-login event'); + window.dispatchEvent(new CustomEvent('show-login')); - // 3. 重置标志位 (3秒后才允许再次触发) setTimeout(() => { isRelogging = false; + console.log('[Auth] Reset isRelogging flag'); }, 3000); }; @@ -576,6 +578,7 @@ request.addResponseInterceptor(async (response, config) => { } const { status } = response; + console.log('[API Interceptor] Response status:', status, 'URL:', config?.url); // ------------------ 修改重点开始 ------------------ @@ -586,10 +589,11 @@ request.addResponseInterceptor(async (response, config) => { // 关键点 2: 必须用 .clone(),因为流只能读一次。读了克隆的,原版 response 还能留给外面用 // 关键点 3: 必须 await,因为读取流是异步的 resData = await response.clone().json(); + console.log('[API Interceptor] Response data:', resData); } catch (e) { // 如果后端返回的不是 JSON (比如 404 HTML 页面,或者空字符串),json() 会报错 // 这里捕获异常,保证 resData 至少是个空对象,不会导致后面取值 crash - console.warn('响应体不是有效的JSON:', e); + console.warn('[API Interceptor] 响应体不是有效的JSON:', e); resData = {}; } @@ -597,6 +601,7 @@ request.addResponseInterceptor(async (response, config) => { // 优先取后端 body 里的 business code,没有则取 HTTP status const code = resData.code ?? status; const codeStr = String(code); + console.log('[API Interceptor] Extracted code:', code, 'codeStr:', codeStr); // 3. 判断成功 (根据你的后端约定:200/0 为成功) // 如果是成功状态,直接返回 response,不拦截 @@ -621,7 +626,9 @@ request.addResponseInterceptor(async (response, config) => { } // 7. 处理 Token 过期 / 未登录 - if (AUTH_ERR_CODES.includes(code) || AUTH_ERR_CODES.includes(codeStr)) { + const isAuthError = AUTH_ERR_CODES.includes(code) || AUTH_ERR_CODES.includes(codeStr); + console.log('[API Interceptor] Is auth error?', isAuthError, 'AUTH_ERR_CODES:', AUTH_ERR_CODES); + if (isAuthError) { handleLoginRedirect(); }