如何在Next.js 14客户端侧实现App Router受保护路由的JWT校验?
Next.js 14 App Router 实现JWT前置校验
完全可以实现页面访问前的JWT有效性校验,下面分两种场景给出具体实现方案:
场景1:客户端侧校验(适配localStorage存储令牌)
因为你的JWT存在localStorage(仅客户端可访问),所以可以通过客户端组件的路由守卫来处理:
方式1:封装全局校验组件
创建一个AuthGuard.tsx客户端组件,用于包裹需要校验的页面:
'use client'; import { useEffect } from 'react'; import { useRouter } from 'next/navigation'; const AuthGuard = ({ children }: { children: React.ReactNode }) => { const router = useRouter(); useEffect(() => { const validateToken = async () => { const token = localStorage.getItem('jwt_token'); if (!token) { router.push('/login'); return; } // 调用后端接口校验令牌有效性 try { const res = await fetch('/api/auth/validate', { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } }); if (!res.ok) { localStorage.removeItem('jwt_token'); router.push('/login'); } } catch (err) { localStorage.removeItem('jwt_token'); router.push('/login'); } }; validateToken(); }, [router]); return <>{children}</>; }; export default AuthGuard;
然后在需要保护的页面(客户端组件)中使用:
'use client'; import AuthGuard from '@/components/AuthGuard'; const DashboardPage = () => { return ( <AuthGuard> {/* 页面内容 */} <h1>仪表盘</h1> </AuthGuard> ); }; export default DashboardPage;
方式2:使用layout.tsx全局校验
如果大部分页面都需要校验,可以在根layout.tsx(或子路由的layout.tsx)中加入校验逻辑,注意要标记为客户端组件:
'use client'; import { useEffect } from 'react'; import { useRouter } from 'next/navigation'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { const router = useRouter(); useEffect(() => { const token = localStorage.getItem('jwt_token'); // 排除登录页面,避免死循环 const currentPath = window.location.pathname; if (!token && currentPath !== '/login') { router.push('/login'); return; } if (token) { // 可选:校验令牌有效性 fetch('/api/auth/validate', { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } }).then(res => { if (!res.ok) { localStorage.removeItem('jwt_token'); router.push('/login'); } }).catch(() => { localStorage.removeItem('jwt_token'); router.push('/login'); }); } }, [router]); return ( <html lang="zh-CN"> <body>{children}</body> </html> ); }
场景2:结合服务器端校验(优化方案)
如果希望在服务器端也做一层校验,建议把JWT同时存在HttpOnly的Cookie中(和localStorage并存),这样中间件可以读取Cookie中的令牌进行校验,避免客户端校验的延迟:
编写middleware.ts(根目录)
import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { // 从Cookie获取令牌 const token = request.cookies.get('jwt_token')?.value; const currentPath = request.nextUrl.pathname; // 排除登录页面和公开路由 if (currentPath === '/login' || currentPath.startsWith('/public')) { return NextResponse.next(); } // 无令牌则重定向到登录页 if (!token) { return NextResponse.redirect(new URL('/login', request.url)); } // 可选:调用后端接口校验令牌有效性(服务器端请求) // const res = await fetch(process.env.NEXT_PUBLIC_API_URL + '/auth/validate', { // headers: { 'Authorization': `Bearer ${token}` } // }); // if (!res.ok) { // return NextResponse.redirect(new URL('/login', request.url)); // } return NextResponse.next(); } // 指定需要校验的路由 export const config = { matcher: ['/dashboard/:path*', '/profile/:path*'], // 按需配置 };
注意事项
- 客户端校验时,页面会短暂渲染后再跳转,若要避免这种情况,可以在校验完成前显示加载态
- 令牌校验接口要返回明确的状态码(比如401表示令牌无效)
- 若使用服务器端中间件,务必确保令牌存储在Cookie中(HttpOnly属性可以防止XSS攻击)
内容的提问来源于stack exchange,提问作者Muhammad Talha
相关产品推荐
相关产品推荐

