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.
This commit is contained in:
Xin Wang
2026-07-10 10:44:33 +08:00
parent 3ed9e1b388
commit 0fa02cc563
4 changed files with 32 additions and 5 deletions

View File

@@ -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 {

View File

@@ -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}</>;

View File

@@ -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<T>(path: string, init?: RequestInit): Promise<T> {
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})`;

View File

@@ -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")) ?? "/";
}