Next.js v15+Auth.js v5:客户端组件页面如何校验用户登录?
客户端组件页面的登录校验方案(Next.js 15 + Auth.js v5)
问题分析
你当前的代码存在几个关键问题:
- 客户端组件中不能调用
getServerSession,这个方法仅适用于服务器组件或API路由 loginRequiredClient的写法有误:useSession返回的是包含data(会话信息)和status(状态:loading/authenticated/unauthenticated)的对象,不是直接的session;且hook不能在async函数中直接await调用- 客户端页面的登录校验逻辑不完整,未处理会话加载状态
推荐解决方案:服务器端路由组拦截 + 客户端状态处理
步骤1:给私有路由组添加服务器端Layout
在app/(private)/layout.tsx中添加全局登录校验,确保整个私有路由组的页面都被服务器端拦截:
import { authConfig } from "@/app/lib/auth"; import { loginIsRequiredServer } from "@/app/lib/loginIsRequiredServer"; import type { ReactNode } from "react"; export default async function PrivateLayout({ children }: { children: ReactNode }) { // 服务器端校验登录,未登录则重定向到首页 await loginIsRequiredServer(); return <>{children}</>; }
步骤2:修改客户端页面的登录状态处理
更新app/(private)/customers/page.tsx,正确使用客户端会话钩子:
"use client"; import { useSession } from "next-auth/react"; import useSWR from "swr"; // 定义客户类型(如果没有全局声明的话) interface CustomerI { customer_id: string; customer_name: string; } export default function CustomersPage() { // 获取客户端会话状态 const { data: session, status } = useSession(); // 处理会话加载中状态 if (status === "loading") { return ( <div> <div className="loading"></div> <span>加载中...</span> </div> ); } // 兜底处理:服务器端已拦截,这里仅做客户端状态兼容 if (!session) { return <div>请先登录</div>; } // 获取客户数据 const { data: customersData, error: customersError, isLoading: customersIsLoading } = useSWR("/customers/api-customers/api-get-customers/"); if (customersError) return <div className="error_smal"><span>加载失败</span></div>; if (customersIsLoading) return ( <div> <div className="loading"></div> <span>加载客户数据中...</span> </div> ); return ( <> <h1>客户列表</h1> <ul> {customersData?.data.map((customer: CustomerI) => ( <li className="list-none" key={customer.customer_id}> <p>{customer.customer_name}</p> </li> ))} </ul> </> ); }
可选:纯客户端校验钩子(适合单独页面)
如果不需要全局路由拦截,可修正loginRequiredClient为正确的自定义钩子:
"use client"; import { useSession } from "next-auth/react"; import { useRouter } from "next/navigation"; import { useEffect } from "react"; export function useLoginRequiredClient() { const { data: session, status } = useSession(); const router = useRouter(); useEffect(() => { // 未登录状态下跳转到首页 if (status === "unauthenticated") { router.push("/"); } }, [status, router]); return { session, status }; }
在客户端页面中使用:
"use client"; import { useLoginRequiredClient } from "@/app/lib/loginRequiredClient"; // ...其他导入 export default function CustomersPage() { const { session, status } = useLoginRequiredClient(); if (status === "loading") { return <div>加载中...</div>; } // ...后续逻辑 }
关键说明
- 服务器端校验优先:服务器端的路由组拦截是最安全的方案,避免客户端校验被绕过
- 客户端
useSession仅用于处理前端状态(如加载中、用户信息展示),核心权限控制交给服务器端 - 移除客户端组件中对
getServerSession的调用,该方法仅能在服务器环境运行
内容的提问来源于stack exchange,提问作者Europa
相关产品推荐
相关产品推荐

