如何在Django与Next.js 13之间实现身份认证?
Django 与 Next.js 13 基于 JWT 的身份认证实现方案
当然可以在两者之间搭建稳定的身份认证系统,下面是针对 Next.js 13 服务端组件特性适配的 JWT 实现方案:
一、Django 端:JWT 令牌的生成与验证
1. 依赖安装与基础配置
首先安装 Django 生态的 JWT 工具库:
pip install djangorestframework-simplejwt django-cors-headers
在 settings.py 中配置核心参数:
INSTALLED_APPS = [ # ... 其他依赖 'rest_framework', 'rest_framework_simplejwt', 'corsheaders', ] # CORS 配置(允许 Next.js 域名跨域请求) CORS_ALLOWED_ORIGINS = [ "http://localhost:3000", # 开发环境 # 生产环境添加你的 Next.js 域名 ] CORS_ALLOW_CREDENTIALS = True # REST Framework 认证配置 REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ), } # JWT 令牌过期时间配置 from datetime import timedelta SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15), 'REFRESH_TOKEN_LIFETIME': timedelta(days=7), 'ROTATE_REFRESH_TOKENS': True, 'BLACKLIST_AFTER_ROTATION': True, }
2. 路由配置
在项目的 urls.py 中添加 JWT 相关接口:
from rest_framework_simplejwt.views import ( TokenObtainPairView, TokenRefreshView, TokenVerifyView, ) urlpatterns = [ # ... 其他路由 path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'), # 新增一个获取当前用户信息的接口(用于前端验证) path('api/user/', YourUserView.as_view(), name='user_info'), ]
二、Next.js 13 端:令牌存储与认证逻辑
Next.js 13 的服务端组件无法直接访问浏览器存储,因此需要区分客户端/服务端场景设计令牌存储策略:
1. 令牌存储规则
- 访问令牌(Access Token):
- 客户端组件:存储在
localStorage(方便前端直接读取),但需注意 XSS 风险;也可存储在非 HttpOnly Cookie 中,兼顾服务端组件读取。 - 服务端组件:通过请求头或 Cookie 获取令牌,无法直接访问
localStorage。
- 客户端组件:存储在
- 刷新令牌(Refresh Token):必须存储在 HttpOnly、Secure、SameSite 的 Cookie 中,仅允许服务端读取,避免 XSS 攻击(刷新令牌有效期长,风险更高)。
2. 登录流程(客户端组件实现)
创建客户端登录表单,提交用户名密码到 Django 接口,处理令牌存储:
'use client'; import { useState } from 'react'; import { useRouter } from 'next/navigation'; export default function LoginForm() { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const router = useRouter(); const handleLogin = async (e) => { e.preventDefault(); try { const res = await fetch('http://your-django-api/api/token/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', // 允许携带 Cookie body: JSON.stringify({ username, password }) }); const data = await res.json(); if (res.ok) { // 存储访问令牌到 localStorage localStorage.setItem('access_token', data.access); // 刷新令牌由 Django 通过 Set-Cookie 头自动设置为 HttpOnly Cookie router.push('/dashboard'); } } catch (err) { console.error('登录失败:', err); } }; return ( <form onSubmit={handleLogin} className="space-y-4"> <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} placeholder="用户名" required /> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="密码" required /> <button type="submit" className="btn">登录</button> </form> ); }
3. 服务端组件的认证数据获取
服务端组件通过 headers() 或 cookies() 函数获取令牌,调用 Django 接口验证身份:
import { cookies } from 'next/headers'; export default async function Dashboard() { const cookieStore = cookies(); const accessToken = cookieStore.get('access_token')?.value || localStorage.getItem('access_token'); // 兼容客户端存储场景 let user = null; if (accessToken) { const res = await fetch('http://your-django-api/api/user/', { headers: { Authorization: `Bearer ${accessToken}` }, credentials: 'include' }); if (res.ok) { user = await res.json(); } } return ( <div className="container mx-auto"> {user ? ( <h1 className="text-xl">欢迎,{user.username}</h1> ) : ( <p>请先登录</p> )} </div> ); }
4. 中间件自动处理令牌刷新
使用 Next.js Middleware 拦截需要认证的路由,自动检查令牌有效性并刷新:
import { NextResponse } from 'next/server'; export async function middleware(request) { // 放行登录页等无需认证的路由 if (request.nextUrl.pathname === '/login') { return NextResponse.next(); } const cookieStore = request.cookies; const accessToken = cookieStore.get('access_token')?.value; const refreshToken = cookieStore.get('refresh_token')?.value; // 无令牌直接跳登录页 if (!accessToken) { return NextResponse.redirect(new URL('/login', request.url)); } // 验证访问令牌有效性 const verifyRes = await fetch('http://your-django-api/api/token/verify/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token: accessToken }) }); if (verifyRes.ok) { return NextResponse.next(); } else { // 访问令牌过期,尝试用刷新令牌获取新令牌 if (refreshToken) { const refreshRes = await fetch('http://your-django-api/api/token/refresh/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh: refreshToken }) }); if (refreshRes.ok) { const newTokens = await refreshRes.json(); const response = NextResponse.next(); // 更新访问令牌到 Cookie response.cookies.set('access_token', newTokens.access, { secure: process.env.NODE_ENV === 'production', sameSite: 'strict', maxAge: 15 * 60 // 15分钟有效期 }); return response; } } // 刷新令牌也失效,跳登录页 return NextResponse.redirect(new URL('/login', request.url)); } } // 指定需要认证的路由匹配规则 export const config = { matcher: ['/dashboard/:path*', '/profile/:path*'] };
三、关键注意事项
- 安全防护:Refresh Token 必须设置为 HttpOnly + Secure Cookie,Access Token 若存在 localStorage,需在前端添加 XSS 防护逻辑(如输入过滤)。
- 跨域配置:确保 Django 的 CORS 配置允许 Next.js 域名,且请求携带
credentials: 'include'。 - 令牌失效处理:Middleware 自动刷新令牌可提升用户体验,避免频繁登录。
内容的提问来源于stack exchange,提问作者Saif
相关产品推荐
相关产品推荐

