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:
@@ -4,6 +4,7 @@ import { FormEvent, useEffect, useState } from "react";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { LockKeyhole, LogIn, Video } from "lucide-react";
|
import { LockKeyhole, LogIn, Video } from "lucide-react";
|
||||||
import { useAuth } from "@/components/auth/AuthProvider";
|
import { useAuth } from "@/components/auth/AuthProvider";
|
||||||
|
import { readReturnToFromLocation } from "@/lib/auth-redirect";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@@ -24,7 +25,7 @@ export default function LoginPage() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && user) {
|
if (!loading && user) {
|
||||||
router.replace("/");
|
router.replace(readReturnToFromLocation());
|
||||||
}
|
}
|
||||||
}, [loading, router, user]);
|
}, [loading, router, user]);
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ export default function LoginPage() {
|
|||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
try {
|
try {
|
||||||
await login(username.trim(), password);
|
await login(username.trim(), password);
|
||||||
router.replace("/");
|
router.replace(readReturnToFromLocation());
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "登录失败");
|
setError(err instanceof Error ? err.message : "登录失败");
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { ReactNode } from "react";
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
import { AppShell } from "@/components/layout/AppShell";
|
import { AppShell } from "@/components/layout/AppShell";
|
||||||
|
import { loginPathWithReturnTo } from "@/lib/auth-redirect";
|
||||||
import { useAuth } from "./AuthProvider";
|
import { useAuth } from "./AuthProvider";
|
||||||
|
|
||||||
export function AuthGate({ children }: { children: ReactNode }) {
|
export function AuthGate({ children }: { children: ReactNode }) {
|
||||||
@@ -15,9 +16,9 @@ export function AuthGate({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !user && !isLoginPage) {
|
if (!loading && !user && !isLoginPage) {
|
||||||
router.replace("/login");
|
router.replace(loginPathWithReturnTo(pathname));
|
||||||
}
|
}
|
||||||
}, [isLoginPage, loading, router, user]);
|
}, [isLoginPage, loading, pathname, router, user]);
|
||||||
|
|
||||||
if (isLoginPage) {
|
if (isLoginPage) {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
* 注意:api_key 读取时后端永远打码,写回打码占位符表示"不改 key"(写时哨兵)。
|
* 注意:api_key 读取时后端永远打码,写回打码占位符表示"不改 key"(写时哨兵)。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { loginPathWithReturnTo } from "@/lib/auth-redirect";
|
||||||
|
|
||||||
export const API_BASE =
|
export const API_BASE =
|
||||||
process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8000";
|
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" &&
|
typeof window !== "undefined" &&
|
||||||
!path.startsWith("/api/auth/")
|
!path.startsWith("/api/auth/")
|
||||||
) {
|
) {
|
||||||
window.location.href = "/login";
|
window.location.href = loginPathWithReturnTo(
|
||||||
|
window.location.pathname + window.location.search,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
let detail = `请求失败 (${res.status})`;
|
let detail = `请求失败 (${res.status})`;
|
||||||
|
|||||||
21
frontend/src/lib/auth-redirect.ts
Normal file
21
frontend/src/lib/auth-redirect.ts
Normal 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")) ?? "/";
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user