You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 13(App Router)中配置受保护路由的技术问题

Next.js App Router 受保护路由配置方案咨询

我正在开发一个全栈应用,前端采用Next.js(App Router),后端使用Express.js搭配Passport.js本地策略结合会话完成身份认证。后端的/login、/logout和/user验证路由已经配置完成,目前的难点集中在Next.js前端的受保护路由(如/profile)配置上。

我已经编写了包含login、logout、fetchUser函数的AuthProvider,并将其挂载到RootLayout中。以下是相关代码:

目录结构

应用目录结构大致如下:

  • app/
    • auth/
      • AuthProvider.tsx
    • profile/
      • page.tsx
    • layout.tsx(即RootLayout)
    • globals.css

AuthProvider.tsx 代码

"use client";

import { createContext, useContext, useState, useEffect } from "react";
import { useRouter } from "next/navigation";

type User = Record<string, unknown> | null;

interface AuthContextType {
  isAuthenticated: boolean;
  user: User;
  login: (username: string, password: string) => Promise<void>;
  isLoading: boolean;
  logout: () => Promise<void>;
}

const AuthContext = createContext<AuthContextType>({
  isAuthenticated: false,
  user: null,
  login: async () => {},
  isLoading: true,
  logout: async () => {}
});


function AuthProvider({ children }: {
  children: React.ReactNode;
}) {
  const [user, setUser] = useState(null);
  const [isLoading, setLoading] = useState(true);
  const router = useRouter();

  useEffect(() => {
    fetchUser();
  }, []);

  const fetchUser = async () => {
    console.log("user fetched");
    
    const response = await fetch("http://localhost:4000/user", {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
      },
      credentials: "include",
    });

    if (response.status === 200) {
      const data = await response.json();
      setUser(data);
    } else {
      setUser(null);
    }
    setLoading(false);
  };

  const login = async (username: string, password: string) => {
    const response = await fetch("http://localhost:4000/login", {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ username, password }),
      credentials: "include",
    });

    if (response.status === 200) {
      await fetchUser();
      router.push("/profile");
    }
  };

  const logout = async () => {
    console.log("Sessão destruída");
    await fetch("http://localhost:4000/logout", {
      method: "POST",
      credentials: "include",
    });
    
    setUser(null);
    router.push('/');
  };

  return (
    <AuthContext.Provider value={{ isAuthenticated: !!user, user, login, isLoading, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = () => useContext(AuthContext);

export { AuthProvider };

RootLayout 代码

import "./globals.css";
import { Inter } from "next/font/google";
import { AuthProvider } from "./auth/AuthProvider";

const inter = Inter({ subsets: ["latin"] });


export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="pt-BR">
      <AuthProvider>
        <body className={inter.className}>{children}</body>
      </AuthProvider>
    </html>
  );
}

我曾考虑在/profile页面中引入useAuth()钩子,判断用户是否已认证,未认证则重定向到登录页,但这样需要将整个页面转为客户端组件,我认为这并非最佳实践。网上多数方案针对旧版页面结构,和我的代码适配性差,恳请提供合适的解决方案。


内容的提问来源于stack exchange,提问作者Pedro H Jalowitzki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 11:13:20