Next.js中Protected Route(受保护路由)
文章目录
- 前言
- ✅ Next.js 中实现受保护路由的方式(按场景拆解)
- ✅ 1. `app/` 目录(React Server Components + 中间件支持)
- 🧠 原理一:使用 `Middleware` 拦截请求
- 示例:`middleware.ts`
- 配置 `matcher`(可选)
- ✅ 2. `app/` 目录中的 Server Component 页面中校验
- ✅ 3. `pages/` 目录传统方式:HOC 保护组件
- ✅ 4. 使用 Layout 组件统一包裹所有受保护页面
- ✅ 常用令牌/登录状态判断方法
- ✅ 总结:Next.js 实现 Protected Route 的几种机制
- 🧱 文件结构建议:
- ✅ 1. `lib/auth.ts` 示例(伪代码)
- ✅ 2. `app/(protected)/layout.tsx` 实现核心保护逻辑
- ✅ 3. `app/(protected)/dashboard/page.tsx` 示例受保护页面
- ✅ 4. `app/(auth)/login/page.tsx` 示例登录页
- ✅ ✅ 总结:受保护路由实现流程
前言
在 Next.js 中,Protected Route(受保护路由) 的核心目的是:
🛡️ 限制未登录用户访问某些页面,并在需要时重定向到登录页。
✅ Next.js 中实现受保护路由的方式(按场景拆解)
✅ 1. app/
目录(React Server Components + 中间件支持)
适用于 Next.js 13+/14+ 新架构
🧠 原理一:使用 Middleware
拦截请求
在请求进入页面前,判断用户是否有权限,若无则跳转。
示例:middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'export function middleware(request: NextRequest) {const token = request.cookies.get('token')?.valueconst protectedPaths = ['/dashboard', '/profile']const isProtected = protectedPaths.some(path =>request.nextUrl.pathname.startsWith(path))if (isProtected && !token) {const loginUrl = new URL('/login', request.url)return NextResponse.redirect(loginUrl)}return NextResponse.next()
}
配置 matcher
(可选)
export const config = {matcher: ['/dashboard/:path*', '/profile/:path*']
}
✅ 2. app/
目录中的 Server Component 页面中校验
适用于 app/dashboard/page.tsx
这种服务端渲染页面
import { redirect } from 'next/navigation'
import { getUserFromCookie } from '@/lib/auth'export default async function DashboardPage() {const user = await getUserFromCookie()if (!user) redirect('/login') // 服务器重定向,跳过渲染return <div>Hello, {user.name}</div>
}
✅ 3. pages/
目录传统方式:HOC 保护组件
适用于 Next.js pages/
路由老架构。
function withAuth(Component) {return function AuthWrapper(props) {const isLoggedIn = typeof window !== 'undefined' && localStorage.getItem('token')if (!isLoggedIn) {if (typeof window !== 'undefined') {window.location.href = '/login'}return null}return <Component {...props} />}
}
然后:
export default withAuth(MyPage)
⚠️ 缺点:客户端重定向,可能会闪烁。
✅ 4. 使用 Layout 组件统一包裹所有受保护页面
在 app/
中创建:
// app/(auth)/layout.tsx → 公共 Layout(登录页)
export default function AuthLayout({ children }) {return <>{children}</>
}// app/(protected)/layout.tsx → 需要登录的 Layout
import { redirect } from 'next/navigation'
import { getUserFromCookie } from '@/lib/auth'export default async function ProtectedLayout({ children }) {const user = await getUserFromCookie()if (!user) redirect('/login')return <>{children}</>
}
然后受保护页面放在 /app/(protected)/dashboard/page.tsx
✅ 常用令牌/登录状态判断方法
场景 | 判断依据 |
---|---|
服务端请求 | cookies().get('token') |
客户端导航 | localStorage.getItem('token') |
NextAuth.js | useSession() / getServerSession() |
Auth0/Supabase | user?.id / auth.user() 等 SDK 状态 |
✅ 总结:Next.js 实现 Protected Route 的几种机制
方法 | 场景 | 优点 | 缺点 |
---|---|---|---|
✅ middleware.ts | 所有页面预拦截 | 全局无闪烁 | 逻辑较简单(仅可读 cookie) |
✅ Server Redirect(redirect() ) | app/ 目录 | 渲染前跳转,安全 | SSR-only,不适合纯客户端页面 |
⚠️ 客户端 HOC | pages/ | 适合老架构 | 会闪烁,前端可见 |
✅ layout.tsx 内部控制 | app/ 分组控制 | 灵活、分层控制 | 依赖 SSR,适配复杂逻辑 |
Next.js app/
目录结构(RSC 架构) 的完整【受保护路由方案模板】
涵盖:
- ✅ 使用 Layout 封装受保护区域(如
/dashboard
) - ✅ 服务端安全校验(使用
cookies()
获取 token) - ✅ 自动跳转到
/login
- ✅ 可扩展集成真实认证逻辑(如 JWT、Session、Supabase、NextAuth 等)
🧱 文件结构建议:
app/
├─ (auth)/ ← 登录、注册等不需要登录的页面
│ └─ login/page.tsx
├─ (protected)/ ← 所有需要登录的受保护页面
│ ├─ layout.tsx ← 在这里实现登录校验逻辑
│ └─ dashboard/page.tsx
lib/
└─ auth.ts ← 封装 token 解析 / 获取当前用户
✅ 1. lib/auth.ts
示例(伪代码)
你可以接入实际的鉴权逻辑(如 JWT 解密、查数据库、调用 API):
import { cookies } from 'next/headers'export async function getCurrentUser() {const cookieStore = cookies()const token = cookieStore.get('token')?.valueif (!token) return null// 示例:从 token 解密用户信息try {const user = JSON.parse(atob(token.split('.')[1]))return user} catch (e) {return null}
}
✅ 2. app/(protected)/layout.tsx
实现核心保护逻辑
// app/(protected)/layout.tsx
import { redirect } from 'next/navigation'
import { getCurrentUser } from '@/lib/auth'export default async function ProtectedLayout({children,
}: {children: React.ReactNode
}) {const user = await getCurrentUser()if (!user) {redirect('/login') // ⛔ 未登录,跳转登录页}return <>{children}</>
}
✅ 3. app/(protected)/dashboard/page.tsx
示例受保护页面
export default function DashboardPage() {return (<div className="p-4"><h1 className="text-xl font-bold">欢迎进入控制台</h1></div>)
}
所有在
(protected)/
分组下的页面都会自动被该layout.tsx
包裹并执行鉴权。
✅ 4. app/(auth)/login/page.tsx
示例登录页
你可以在此页设置登录状态并存入 cookie(建议使用 Set-Cookie
响应头或服务端设置):
'use client'
import { useRouter } from 'next/navigation'export default function LoginPage() {const router = useRouter()const handleLogin = async () => {// 假设后台返回 tokendocument.cookie = `token=your_token; path=/`router.push('/dashboard')}return (<div><h1>登录</h1><button onClick={handleLogin}>一键登录</button></div>)
}