Django的CSRF token验证失败怎么办

文章导读
Django 的 CSRF token 验证失败通常会返回 403 错误,页面提示“CSRF verification failed. Request aborted.”。这个问题的根源往往不在 Django 配置本身,而是前后端 token 传递链路或中间件顺序出了岔子。下面按排查顺序给出具体处理路径,每步都附带验证方法和风险边界。
📋 目录
  1. 先确认模板里有没有 token
  2. AJAX 请求的 token 传递
  3. 核实 cookie 和 header 的名称是否对得上
  4. 中间件顺序和缓存的影响
  5. 跨域和 HTTPS 下的特殊设置
  6. 重头戏:浏览器禁用了 cookie 怎么办
A A

Django 的 CSRF token 验证失败通常会返回 403 错误,页面提示“CSRF verification failed. Request aborted.”。这个问题的根源往往不在 Django 配置本身,而是前后端 token 传递链路或中间件顺序出了岔子。下面按排查顺序给出具体处理路径,每步都附带验证方法和风险边界。

先确认模板里有没有 token

首先确认表单中是否包含 {% csrf_token %} 标签。该标签会渲染为一个隐藏的 input 字段,name 为 'csrfmiddlewaretoken'。如果页面源代码中找不到这个字段,可能是模板未正确加载或使用了不兼容的模板引擎。另外,检查视图函数是否使用了 @csrf_exempt 装饰器,它会跳过 CSRF 验证,但通常仅在特殊场景下使用。

如果表单来自第三方库生成的 HTML,或者你用了 JavaScript 动态拼接表单,必须手动插入这个 hidden input。验证方式很简单:在浏览器里右键查看页面源代码,搜索“csrfmiddlewaretoken”。如果没找到,优先检查模板是否继承了包含该标签的父模板,或者模板引擎是否解析了 Django 模板标签。如果是 AJAX 提交表单,参考下一节。

AJAX 请求的 token 传递

对于 AJAX 请求,需要在请求头中设置 X-CSRFToken 值。可以从 cookie 中读取 token,假设 cookie 名为 'csrftoken'(默认)。使用 JavaScript 获取 cookie 并添加到 XMLHttpRequest 的 setRequestHeader 中。注意 cookie 可能带有 path 或 domain 属性,确保前端能正确读取。如果使用 fetch API,同样需要手动添加 header。

常见的踩坑点是 cookie 的 HttpOnly 或 Secure 标记导致 JavaScript 无法读取。默认情况下 Django 的 CSRF cookie 不设置 HttpOnly,但如果你在 settings.py 里改了 CSRF_COOKIE_HTTPONLY = True,就必须改用其他方式(比如从 DOM 中读取 token)。验证方法:在浏览器控制台输入 document.cookie,如果能看到 csrftoken 的值,说明前端可以读到;否则检查 cookie 的路径和域名是否匹配当前页面。另外,如果后端启用了 CSRF_USE_SESSIONS = True,token 不会出现在 cookie 中,此时需要从模板的 {% csrf_token %} 标签里提取值,或者通过 API 返回 token。

核实 cookie 和 header 的名称是否对得上

核实 Django 的 CSRF_COOKIE_NAME 和 CSRF_HEADER_NAME 设置是否与前端期望的名称一致。默认情况下,cookie 名称是 'csrftoken',header 名称是 'X-CSRFToken',但可能被修改。另外,检查 CSRF_USE_SESSIONS 是否设为 True,此时 token 存储在 session 中而非 cookie,需相应调整前端读取方式。

如果修改了默认名称,前后端必须同步修改。例如,你自定义了 CSRF_COOKIE_NAME = 'mycsrf',前端就需要从名为 'mycsrf' 的 cookie 中取值,并把请求头设为 X-CSRFToken(或者你同时改了 CSRF_HEADER_NAME)。验证方法:在浏览器开发者工具的网络标签里,查看 POST 请求的请求头,确认 X-CSRFToken 的值是否存在且与 cookie 里的值一致。如果请求头缺失,说明前端没正确设置;如果值不一致,可能是 cookie 被重新设置了(比如多个 tab 导致 token 刷新)。

中间件顺序和缓存的影响

确保 CsrfViewMiddleware 出现在 MIDDLEWARE 列表的正确位置:必须在 SessionMiddleware 之后,因为 CSRF token 依赖于 session 或 cookie。如果使用了缓存中间件或缓存视图,注意 CSRF token 可能因缓存而失效,导致提交表单时 token 不匹配。此外,反向代理或负载均衡可能改变请求的 host,需设置 CSRF_TRUSTED_ORIGINS 包含真实来源。

Django的CSRF token验证失败怎么办

常见的一个情况是使用了 CacheMiddleware 后,缓存的页面中 CSRF token 是旧值,用户提交时 token 已经过期。建议对包含表单的页面不要使用全页面缓存,或者使用 @never_cache 装饰器。检查中间件顺序:MIDDLEWARE 列表中越靠前的越早执行,CsrfViewMiddleware 应放在 SessionMiddleware 后面(通常默认就是对的,但如果你自定义排序,需要验证)。验证方法:在视图里打印 request.META.get('CSRF_COOKIE')request.META.get('HTTP_X_CSRFTOKEN'),对比它们是否一致。

跨域和 HTTPS 下的特殊设置

当遇到跨域 POST 请求时,必须将发起请求的域名添加到 CSRF_TRUSTED_ORIGINS 列表中。例如,如果前端运行在 http://localhost:3000,后端在 http://localhost:8000,需设置 CSRF_TRUSTED_ORIGINS = ['http://localhost:3000']。注意该设置仅影响 CSRF 的 Referer 检查,不影响同源策略。另外,可以暂时去掉该设置并通过浏览器的开发者工具查看请求头中的 Referer 值是否匹配。

如果后端部署在 HTTPS 下,CSRF cookie 的 Secure 标记默认是 False,建议设置为 True:CSRF_COOKIE_SECURE = True,但这要求前端也通过 HTTPS 访问,否则 cookie 不会被发送。同样,CSRF_COOKIE_SAMESITE 设置也可能影响跨域请求,默认是 'Lax',可以通过设为 'None' 并配合 Secure 来允许跨站发送。验证方法:在浏览器控制台查看 Application > Cookies,确认 csrftoken 的 SameSite 和 Secure 属性是否符合预期。如果请求跨域且没有看到 cookie,大概率是 SameSite 或 Secure 设置问题。

重头戏:浏览器禁用了 cookie 怎么办

如果浏览器禁用了 cookie,则基于 cookie 的 CSRF 防护完全失效。这种情况下,可以考虑使用 CSRF_USE_SESSIONS 将 token 保存在 session 中,但 session 本身也可能依赖 cookie(如 sessionid cookie)。另一种替代方案是使用 Django REST framework 的 TokenAuthentication 或其他非 cookie 认证机制作为补充。

注意:CSRF_USE_SESSIONS = True 会让 token 存储在服务器端 session 中,而 session 的追踪仍然需要 sessionid cookie。如果浏览器完全禁止所有 cookie,那么 session 也无法工作。此时可以考虑将 CSRF token 放在请求的 URL 参数中(但安全性降低),或者切换到 API 密钥认证。验证方法:在浏览器设置中禁用 cookie,然后发起 POST 请求,观察是否仍返回 403。如果 403 仍然存在,说明 CSRF 验证已经绕过了 cookie 依赖?实际上没有 cookie 后,Django 无法获取 token,需要改成其他机制。但这不是推荐做法,只作为边界说明。

以上步骤基本覆盖了绝大多数 CSRF 失败场景。每次调整配置后,重启开发服务器,并用浏览器的无痕模式测试,避免缓存干扰。如果依然失败,可以临时在视图中打印 request.META 中的 CSRF 相关字段,或者启用 Django 的日志记录 django.security.csrf 来获取更详细的错误信息。