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

Django重复提交AJAX表单触发403 CSRF异常的解决咨询

解决Django AJAX表单提交后CSRF验证失败的问题

首先要明确:绝对不要使用csrf_exempt,这会完全关闭CSRF保护,让你的应用暴露在严重的安全风险下。我们有更安全的方法来解决这个问题。

你的问题根源是Django默认会在每次POST请求后旋转(更新)CSRF Token,而首次AJAX提交成功后,页面没有刷新,表单里的旧Token和服务器端新生成的Token不匹配,导致第二次提交验证失败。下面是几个安全的解决方案:


方案1:AJAX成功后更新页面的CSRF Token

这个方法是在AJAX请求成功后,从响应的Cookie中获取新的CSRF Token,然后更新表单中的隐藏csrfmiddlewaretoken字段。

前端实现步骤:

  1. 确保你的AJAX请求正确发送了CSRF Token(通常表单里的隐藏字段已经包含了)。
  2. 在AJAX的success回调中:
    • 清空表单内容
    • 从浏览器Cookie中获取新的CSRF Token
    • 更新表单的csrfmiddlewaretoken字段值

示例代码(用jQuery+js-cookie库简化Cookie操作):

// 假设你的表单ID是trip-creation-form
$('#trip-creation-form').on('submit', function(e) {
  e.preventDefault();
  const formData = $(this).serialize();
  
  $.ajax({
    url: $(this).attr('action'),
    type: 'POST',
    data: formData,
    success: function(response) {
      if (response.status === 'OK') {
        // 清空表单
        $('#trip-creation-form')[0].reset();
        // 获取新的CSRF Token
        const newCsrfToken = Cookies.get('csrftoken');
        // 更新表单的CSRF字段
        $('#trip-creation-form input[name="csrfmiddlewaretoken"]').val(newCsrfToken);
      }
    },
    error: function(xhr) {
      // 处理错误
      console.error('Trip creation failed:', xhr.responseText);
    }
  });
});

如果不想用js-cookie库,可以用原生JS函数获取Cookie:

function getCookie(name) {
  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();
      if (cookie.substring(0, name.length + 1) === (name + '=')) {
        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
        break;
      }
    }
  }
  return cookieValue;
}

// 在success回调中替换成:
const newCsrfToken = getCookie('csrftoken');

方案2:后端返回新的CSRF Token给前端

你可以在form_valid方法中,把新生成的CSRF Token包含在JSON响应里,然后前端用这个值更新表单字段。

修改你的TripCreationView中的form_valid方法:

from django.middleware.csrf import get_token

def form_valid(self, form):
    if self.request.is_ajax():
        form.save()
        # 获取当前请求的新CSRF Token
        new_csrf_token = get_token(self.request)
        return JsonResponse({'status':'OK', 'csrf_token': new_csrf_token})
    # 非AJAX请求的默认处理
    return super().form_valid(form)

然后前端在success回调中使用返回的token:

success: function(response) {
  if (response.status === 'OK') {
    $('#trip-creation-form')[0].reset();
    // 更新CSRF字段
    $('#trip-creation-form input[name="csrfmiddlewaretoken"]').val(response.csrf_token);
  }
}

不推荐的方案:禁用CSRF Token旋转

如果你实在不想处理Token更新(不推荐,因为降低安全性),可以在settings.py中添加:

CSRF_ROTATION = False

这会让Django不再每次POST请求后更新CSRF Token,这样同一个Token可以多次使用。但请注意,这会减少CSRF保护的安全性,不建议在生产环境使用。


为什么不要用csrf_exempt?

如果非要给你的类视图添加csrf_exempt(强烈不推荐),可以用method_decorator装饰器:

from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator

@method_decorator(csrf_exempt, name='dispatch')
class TripCreationView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
    # 你的视图代码...

但再次强调:这会完全关闭这个视图的CSRF保护,攻击者可以伪造用户请求执行恶意操作,比如创建虚假行程,严重威胁用户数据安全。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:25:40