Next.js 13 App Router页面保护:哪种方案更值得推荐?
Next.js 13 App Router页面保护:方案选择与推荐
在Next.js 13 App Router中,两种页面保护方案都是可行的,但推荐根据业务场景选择,全局使用Middleware是更高效、易维护的首选方式,分端保护则适合局部特殊需求的场景。
方案一:分端保护页面
这种方案针对客户端组件和服务端组件分别使用对应的会话校验方式:
客户端页面(useSession)
适合需要在客户端处理会话状态、有动态交互的页面,但存在“内容闪烁”的潜在问题——页面会先渲染,再完成会话校验并跳转,未授权用户可能短暂看到页面内容。
'use client' import { useSession } from 'next-auth/react' import { redirect } from 'next/navigation' const ClientProtectPage = () => { const { data: session } = useSession({ required: true, onUnauthenticated() { redirect('/signin?callbackUrl=/protected/client') } }) return ( <section className='py-24'> <div className='container'> <h1 className='text-2xl font-bold'> This is a <span className='text-emerald-500'>client-side</span>{' '} protected page </h1> <h2 className='mt-4 font-medium'>You are logged in as:</h2> <p className='mt-4'>{session?.user?.name}</p> </div> </section> ) } export default ClientProtectPage
服务端页面(getServerSession)
服务端渲染阶段直接校验会话,不会暴露未授权内容,安全性更高,适合包含敏感数据的页面。缺点是每个需要保护的服务端页面都要重复编写校验逻辑,维护成本随页面数量增加而上升。
import { getServerSession } from 'next-auth/next' import { redirect } from 'next/navigation' import { authOptions } from '../../api/auth/[...nextauth]/route' const ServerProtectedPage = async () => { const session = await getServerSession(authOptions) if (!session) { redirect('/signin?callbackUrl=/protected/server') } return ( <section className='py-24'> <div className='container'> <h1 className='text-2xl font-bold'> This is a <span className='text-emerald-500'>server-side</span>{' '} protected page </h1> <h2 className='mt-4 font-medium'>You are logged in as:</h2> <p className='mt-4'>{session?.user?.name}</p> </div> </section> ) } export default ServerProtectedPage
方案二:Middleware全局保护页面
通过middleware.js统一拦截请求,在页面渲染前完成会话校验,是推荐的全局保护方式。
核心优势
- 一次配置全局生效,避免重复编写校验逻辑,维护成本低;
- 请求到达页面之前就完成校验,彻底解决客户端页面的内容闪烁问题;
- 可以统一控制登录跳转逻辑,比如统一设置回调URL、处理已登录用户访问登录页的跳转。
import { getToken } from "next-auth/jwt" import { withAuth } from "next-auth/middleware" import { NextResponse } from "next/server" export default withAuth( async function middleware(req) { const token = await getToken({ req }) const isAuth = !!token const isAuthPage = req.nextUrl.pathname.startsWith("/login") || req.nextUrl.pathname.startsWith("/register") if (isAuthPage) { if (isAuth) { return NextResponse.redirect(new URL("/dashboard", req.url)) } return null } if (!isAuth) { let from = req.nextUrl.pathname; if (req.nextUrl.search) { from += req.nextUrl.search; } return NextResponse.redirect( new URL(`/login?from=${encodeURIComponent(from)}`, req.url) ); } }, { callbacks: { async authorized() { // 绕过默认授权逻辑,让自定义middleware逻辑始终执行 return true }, }, } ) export const config = { matcher: ["/dashboard/:path*", "/editor/:path*", "/login", "/register"], }
方案选择总结
- 优先选Middleware:如果你的应用大部分页面需要登录保护,或者希望统一管理授权逻辑,Middleware是最优解;
- 分端保护作为补充:如果只有少数页面需要保护,或者某些页面有特殊的授权规则(比如角色权限校验),可以单独使用分端保护,或者在Middleware基础上,在页面内补充更细粒度的权限校验。
内容的提问来源于stack exchange,提问作者Nick Jonas
相关产品推荐
相关产品推荐

