From 0fa02cc563dcc3d3c850e94310e9e095d6af017f Mon Sep 17 00:00:00 2001 From: Xin Wang Date: Fri, 10 Jul 2026 10:44:33 +0800 Subject: [PATCH] Refactor authentication redirection logic - Introduce a new `auth-redirect` module to manage safe return paths for login and redirection. - Update the login page to utilize `readReturnToFromLocation` for redirecting users post-login. - Modify the `AuthGate` component to use `loginPathWithReturnTo` for redirecting unauthorized users, enhancing security and user experience. - Adjust API request handling to incorporate the new redirection logic, ensuring users are directed to the appropriate login page with return paths. --- frontend/src/app/login/page.tsx | 5 +++-- frontend/src/components/auth/AuthGate.tsx | 5 +++-- frontend/src/lib/api.ts | 6 +++++- frontend/src/lib/auth-redirect.ts | 21 +++++++++++++++++++++ 4 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 frontend/src/lib/auth-redirect.ts diff --git a/frontend/src/app/login/page.tsx b/frontend/src/app/login/page.tsx index 2358b59..5113789 100644 --- a/frontend/src/app/login/page.tsx +++ b/frontend/src/app/login/page.tsx @@ -4,6 +4,7 @@ import { FormEvent, useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import { LockKeyhole, LogIn, Video } from "lucide-react"; import { useAuth } from "@/components/auth/AuthProvider"; +import { readReturnToFromLocation } from "@/lib/auth-redirect"; import { Button } from "@/components/ui/button"; import { Card, @@ -24,7 +25,7 @@ export default function LoginPage() { useEffect(() => { if (!loading && user) { - router.replace("/"); + router.replace(readReturnToFromLocation()); } }, [loading, router, user]); @@ -34,7 +35,7 @@ export default function LoginPage() { setSubmitting(true); try { await login(username.trim(), password); - router.replace("/"); + router.replace(readReturnToFromLocation()); } catch (err) { setError(err instanceof Error ? err.message : "登录失败"); } finally { diff --git a/frontend/src/components/auth/AuthGate.tsx b/frontend/src/components/auth/AuthGate.tsx index 6e5ed06..7dad82a 100644 --- a/frontend/src/components/auth/AuthGate.tsx +++ b/frontend/src/components/auth/AuthGate.tsx @@ -4,6 +4,7 @@ import type { ReactNode } from "react"; import { useEffect } from "react"; import { usePathname, useRouter } from "next/navigation"; import { AppShell } from "@/components/layout/AppShell"; +import { loginPathWithReturnTo } from "@/lib/auth-redirect"; import { useAuth } from "./AuthProvider"; export function AuthGate({ children }: { children: ReactNode }) { @@ -15,9 +16,9 @@ export function AuthGate({ children }: { children: ReactNode }) { useEffect(() => { if (!loading && !user && !isLoginPage) { - router.replace("/login"); + router.replace(loginPathWithReturnTo(pathname)); } - }, [isLoginPage, loading, router, user]); + }, [isLoginPage, loading, pathname, router, user]); if (isLoginPage) { return <>{children}; diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index f6bf602..a791f0a 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -5,6 +5,8 @@ * 注意:api_key 读取时后端永远打码,写回打码占位符表示"不改 key"(写时哨兵)。 */ +import { loginPathWithReturnTo } from "@/lib/auth-redirect"; + export const API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8000"; @@ -20,7 +22,9 @@ async function request(path: string, init?: RequestInit): Promise { typeof window !== "undefined" && !path.startsWith("/api/auth/") ) { - window.location.href = "/login"; + window.location.href = loginPathWithReturnTo( + window.location.pathname + window.location.search, + ); } if (!res.ok) { let detail = `请求失败 (${res.status})`; diff --git a/frontend/src/lib/auth-redirect.ts b/frontend/src/lib/auth-redirect.ts new file mode 100644 index 0000000..0051fb7 --- /dev/null +++ b/frontend/src/lib/auth-redirect.ts @@ -0,0 +1,21 @@ +/** Internal path safe to use as a post-login redirect target. */ +export function getSafeReturnTo( + value: string | null | undefined, +): string | null { + if (!value) return null; + if (!value.startsWith("/") || value.startsWith("//")) return null; + if (value === "/login" || value.startsWith("/login?")) return null; + return value; +} + +export function loginPathWithReturnTo(returnTo: string): string { + const safe = getSafeReturnTo(returnTo); + if (!safe) return "/login"; + return `/login?returnTo=${encodeURIComponent(safe)}`; +} + +export function readReturnToFromLocation(): string { + if (typeof window === "undefined") return "/"; + const params = new URLSearchParams(window.location.search); + return getSafeReturnTo(params.get("returnTo")) ?? "/"; +}