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

非Django托管的React前端如何获取Django auth视图的CSRF Token?

问题描述

我使用Django 3.1.1及Django auth contrib模块管理用户,未使用Django模板,而是创建Django API应用以响应React前端的请求。我的settings.py配置如下:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'directory.middleware.extend_token_response.ExtendTokenResponse'
]

#CORS_ORIGIN_ALLOW_ALL = True

ALLOWED_HOSTS = ['127.0.0.1', 'localhost', 'dev.mywebsite.com', 'prod.mywebsite.com', 'map.mywebsite.com']

CORS_ORIGIN_WHITELIST = [
    'http://localhost:3000', 'http://localhost:3001', 'http://127.0.0.1:3000', 'http://127.0.0.1:3001'
]

CORS_EXPOSE_HEADERS = [
    'Refresh-Token', 'Content-Type', 'Authorization', 'X-CSRFToken'
]
CORS_ALLOW_CREDENTIALS = True
CSRF_TRUSTED_ORIGINS = ['localhost', '127.0.0.1']

views.py中的密码重置视图配置:

class ResetPasswordView(SuccessMessageMixin, PasswordResetView):
    email_template_name = 'users/password_reset_email.html'
    subject_template_name = 'users/password_reset_subject'
    success_message = "We've emailed you instructions for setting your password, " \
                      "if an account exists with the email you entered. You should receive them shortly." \
                      " If you don't receive an email, " \
                      "please make sure you've entered the address you registered with, and check your spam folder."
    success_url = reverse_lazy('users-home')

    def post(self, request, *args, **kwargs):
        print("request data %s" % request.data)
        email = request.data.get('email')
        try:
            if User.objects.get(email=email).active:
                print("email: %s " % email)
                return super(ResetPasswordView, self).post(request, *args, **kwargs)
        except:
            # this for if the email is not in the db of the system
            return super(ResetPasswordView, self).post(request, *args, **kwargs)

前端React通过fetch提交密码重置请求:

const handleFormSubmit = (e) => {
    e.preventDefault()
    const csrftoken = getCsrfToken();
    fetch(REACT_APP_PROXY + '/reset_password', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'X-CSRFToken': csrftoken
      },
      body: JSON.stringify({username})
    }).then(resp => resp.json())
    .then(jsob => {
      if(!jsob.ok){
        console.log(jsob)
        setErrors(jsob)
      }
      if(jsob.token){
        sessionStorage.setItem('token', jsob.token)
        setRedirect(true)
      }
    })
  }

问题:由于Django未托管React应用(由Apache托管),首次提交表单时没有CSRF Token,请问如何从Django获取CSRF Token用于提交密码重置表单?


解决方案

方法1:创建专门的CSRF Token接口

在Django中新增视图,直接返回CSRF Token:

# views.py
from django.middleware.csrf import get_token
from django.http import JsonResponse

def get_csrf_token(request):
    csrf_token = get_token(request)
    return JsonResponse({'csrfToken': csrf_token})

在urls.py配置对应路由:

# urls.py
from django.urls import path
from .views import get_csrf_token

urlpatterns = [
    # 其他已有路由...
    path('get-csrf-token/', get_csrf_token, name='get_csrf_token'),
]

前端修改表单提交逻辑,先调用接口获取Token:

// 封装获取CSRF Token的异步函数
async function fetchCsrfToken() {
  const response = await fetch(REACT_APP_PROXY + '/get-csrf-token/', {
    credentials: 'include'  // 必须携带,确保会话Cookie被传递
  });
  const data = await response.json();
  return data.csrfToken;
}

// 更新表单提交函数
const handleFormSubmit = async (e) => {
  e.preventDefault()
  const csrftoken = await fetchCsrfToken();
  fetch(REACT_APP_PROXY + '/reset_password', {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-CSRFToken': csrftoken
    },
    credentials: 'include',  // 维持会话状态
    body: JSON.stringify({username})
  }).then(resp => resp.json())
  .then(jsob => {
    if(!jsob.ok){
      console.log(jsob)
      setErrors(jsob)
    }
    if(jsob.token){
      sessionStorage.setItem('token', jsob.token)
      setRedirect(true)
    }
  })
}

方法2:直接从Django的CSRF Cookie读取

Django默认会在首次访问时设置csrftoken Cookie,无需额外接口,直接从Cookie中提取:

// 重写getCsrfToken函数
function getCsrfToken() {
  let cookieValue = null;
  if (document.cookie && document.cookie !== '') {
    const cookies = document.cookie.split(';');
    for (let i = 0; i < cookies.length; i++) {
      const cookie = cookies[i].trim();
      // 匹配csrftoken开头的Cookie
      if (cookie.substring(0, 10) === 'csrftoken=') {
        cookieValue = decodeURIComponent(cookie.substring(10));
        break;
      }
    }
  }
  return cookieValue;
}

注意:首次加载页面时需要触发Django设置Cookie,可在React组件的useEffect中发送一个简单的GET请求:

import { useEffect } from 'react';

// 页面初始化时执行
useEffect(() => {
  fetch(REACT_APP_PROXY + '/', {
    credentials: 'include'
  });
}, []);

关键配置校验

确保settings.py中以下配置正确:

  • CORS_ALLOW_CREDENTIALS = True(已配置)
  • CSRF_TRUSTED_ORIGINS需包含所有前端域名,生产环境补充:
CSRF_TRUSTED_ORIGINS = ['localhost', '127.0.0.1', 'dev.mywebsite.com', 'prod.mywebsite.com']
  • CORS_EXPOSE_HEADERS包含X-CSRFToken(已配置)

内容的提问来源于stack exchange,提问作者Dave

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 06:46:04