基于Node.js API(JWT)的Next.js多认证与路由角色权限处理咨询
Next.js 多认证、路由与角色权限控制方案(基于JWT后端)
认证方案选型
结合你的技术栈(Next.js + Node.js JWT),推荐两种主流方案,按需选择:
1. 自定义JWT认证流程
适合需要完全控制认证逻辑、无第三方依赖的场景:
- JWT存储:优先使用HttpOnly Cookie存储JWT(比localStorage更安全,避免XSS攻击),后端登录接口返回
Set-Cookie头 - JWT验证:在Next.js的Middleware(全局)或
getServerSideProps(页面级)中验证JWT有效性 - 路由保护:通过Middleware拦截需要认证的路由,未登录或权限不足时重定向
2. NextAuth.js 集成
适合快速搭建认证体系,支持多认证提供商(邮箱密码、OAuth等),同时兼容自定义JWT后端:
- 用Credentials Provider对接你的Node.js登录接口,获取JWT和用户角色
- 配置JWT会话策略,在回调中注入角色信息
- 内置的
useSession钩子和Middleware可快速实现路由保护
核心实现细节
1. 角色权限设计
在Node.js后端生成JWT时,将角色信息(如role: 'admin'/'user'/'editor')加入payload,示例:
// Node.js 后端登录接口 const jwt = require('jsonwebtoken'); app.post('/login', (req, res) => { // 验证用户账号密码逻辑... const user = { id: 1, email: 'user@example.com', role: 'admin' }; const token = jwt.sign(user, process.env.JWT_SECRET, { expiresIn: '1d' }); // 设置HttpOnly Cookie res.cookie('auth_token', token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', maxAge: 86400000 }); res.json({ success: true }); });
2. 全局路由保护(Middleware)
Next.js 13+ 推荐用Middleware实现全局路由拦截,示例:
// middleware.js import { NextResponse } from 'next/server'; import jwt from 'jsonwebtoken'; export function middleware(request) { const token = request.cookies.get('auth_token')?.value; const { pathname } = request.nextUrl; // 公开路由无需验证 if (['/login', '/register', '/'].includes(pathname)) { return NextResponse.next(); } // 未登录重定向到登录页 if (!token) { return NextResponse.redirect(new URL('/login', request.url)); } // 验证JWT并检查角色 try { const decoded = jwt.verify(token, process.env.JWT_SECRET); // 管理员路由仅允许admin角色访问 if (pathname.startsWith('/admin') && decoded.role !== 'admin') { return NextResponse.redirect(new URL('/forbidden', request.url)); } // 普通用户路由允许user/editor/admin访问 if (pathname.startsWith('/dashboard') && !['user', 'editor', 'admin'].includes(decoded.role)) { return NextResponse.redirect(new URL('/forbidden', request.url)); } } catch (err) { // JWT无效或过期,重定向登录 return NextResponse.redirect(new URL('/login', request.url)); } return NextResponse.next(); } // 指定需要拦截的路由 export const config = { matcher: ['/dashboard/:path*', '/admin/:path*', '/profile'], };
3. 页面级权限控制(Pages Router)
如果用Pages Router,可在getServerSideProps中验证权限:
// pages/admin/dashboard.js import jwt from 'jsonwebtoken'; export async function getServerSideProps(context) { const token = context.req.cookies.auth_token; if (!token) { return { redirect: { destination: '/login', permanent: false } }; } try { const decoded = jwt.verify(token, process.env.JWT_SECRET); if (decoded.role !== 'admin') { return { redirect: { destination: '/forbidden', permanent: false } }; } return { props: { user: decoded } }; } catch (err) { return { redirect: { destination: '/login', permanent: false } }; } } export default function AdminDashboard({ user }) { return <div>Welcome Admin: {user.email}</div>; }
4. NextAuth.js 集成示例
// pages/api/auth/[...nextauth].js import NextAuth from 'next-auth'; import CredentialsProvider from 'next-auth/providers/credentials'; export default NextAuth({ session: { strategy: 'jwt' }, providers: [ CredentialsProvider({ credentials: { email: { label: 'Email', type: 'text' }, password: { label: 'Password', type: 'password' } }, async authorize(credentials) { // 调用Node.js后端登录接口 const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/login`, { method: 'POST', body: JSON.stringify(credentials), headers: { 'Content-Type': 'application/json' } }); const data = await res.json(); if (res.ok && data.user) { return { ...data.user, token: data.token }; } return null; } }) ], callbacks: { async jwt({ token, user }) { if (user) { token.role = user.role; token.accessToken = user.token; } return token; }, async session({ session, token }) { session.user.role = token.role; session.accessToken = token.accessToken; return session; } } });
用useSession保护组件:
// components/EditorPanel.js import { useSession } from 'next-auth/react'; import { useRouter } from 'next/router'; import { useEffect } from 'react'; export default function EditorPanel() { const { data: session, status } = useSession(); const router = useRouter(); useEffect(() => { if (status === 'authenticated' && session.user.role !== 'editor' && session.user.role !== 'admin') { router.push('/forbidden'); } }, [session, status, router]); if (status === 'loading') return <div>Loading...</div>; if (!session) return <div>Please login to access this page</div>; return <div>Editor Panel Content</div>; }
方案对比
| 方案 | 优势 | 劣势 |
|---|---|---|
| 自定义JWT认证 | 完全可控,无第三方依赖,灵活性高 | 需手动处理JWT验证、会话管理等细节 |
| NextAuth.js 集成 | 快速搭建,内置多认证方式,减少重复代码 | 依赖第三方库,自定义逻辑受框架限制 |
内容的提问来源于stack exchange,提问作者Satish Baghel
相关产品推荐
相关产品推荐

