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();
}