You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 03:25:03