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

Flask WTForms与JWT集成:POST请求CSRF令牌缺失问题求助

问题分析与解决方向

核心冲突点

Flask-WTF 和 Flask-JWT-Extended 维护两套独立的 CSRF 保护机制:

  • WTForms 的 CSRF 令牌存储在 session 中,通过表单的 csrf_token 字段验证
  • Flask-JWT-Extended 的 CSRF 令牌(csrf_access_token)存储在 Cookie 中,针对受保护的 POST/PUT/DELETE 请求,会强制验证该令牌

你的受保护 POST 路由同时触发了 JWT 的 CSRF 检查,但表单提交时只携带了 WTForms 的 CSRF 令牌,未提供 JWT 要求的令牌,因此抛出错误。

调试步骤

  1. 检查请求数据
    在受保护的 POST 路由中添加打印语句,确认请求是否携带了 JWT 所需的 CSRF 令牌:

    @jwt_required_and_not_revoked
    def post(self):
        print("Request Headers:", request.headers)
        print("Request Form Data:", request.form)
        form = SolarSearchForm()
        # ... 后续代码
    

    重点查看是否存在 X-CSRF-TOKEN 请求头,或表单中是否有 csrf_access_token 字段。

  2. 验证 JWT 配置
    检查 Flask 配置中与 JWT CSRF 相关的参数:

    app.config["JWT_COOKIE_CSRF_PROTECT"] = True  # 该配置会强制对POST请求检查CSRF
    app.config["JWT_CSRF_IN_COOKIES"] = True
    app.config["JWT_ACCESS_CSRF_HEADER_NAME"] = "X-CSRF-TOKEN"  # JWT默认检查的请求头
    

    若 JWT_COOKIE_CSRF_PROTECT 为 True,则必须提供对应的 CSRF 令牌。

  3. 检查登录流程的 Cookie 设置
    你当前手动设置 access_token_cookie,但未使用 Flask-JWT-Extended 提供的工具方法,可能导致 csrf_access_token 与 access_token 不关联:
    替换登录路由中的 Cookie 设置代码:

    from flask_jwt_extended import set_access_cookies, set_refresh_cookies
    
    # 原代码:
    # response.set_cookie('access_token_cookie', access_token)
    # response.set_cookie('refresh_token_cookie', refresh_token)
    
    # 替换为:
    set_access_cookies(response, access_token)
    set_refresh_cookies(response, refresh_token)
    

    该方法会自动生成并设置匹配的 csrf_access_token Cookie,确保令牌关联正确。

解决方案

方案1:同时提交两套 CSRF 令牌

在表单模板中添加 JWT CSRF 令牌的隐藏字段,同时提交 WTForms 和 JWT 的令牌:

<form method="POST">
    {{ form.hidden_tag() }}  <!-- WTForms的CSRF令牌 -->
    <input type="hidden" name="csrf_access_token" value="{{ request.cookies.get('csrf_access_token') }}">
    <!-- 其他表单字段 -->
    <button type="submit">提交</button>
</form>

或者通过 AJAX 提交时,在请求头中携带 JWT CSRF 令牌:

const csrfToken = document.cookie.split('csrf_access_token=')[1].split(';')[0];
fetch('/your-protected-route', {
    method: 'POST',
    headers: {
        'X-CSRF-TOKEN': csrfToken,
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new FormData(form)
});

方案2:关闭特定路由的 JWT CSRF 检查

如果已通过 WTForms 实现 CSRF 保护,不需要重复验证,可以在路由装饰器中关闭 JWT 的 CSRF 检查(需确认 Flask-JWT-Extended 版本,新版本参数为 protect_csrf=False):

@jwt_required_and_not_revoked(protect_csrf=False)
def post(self):
    form = SolarSearchForm()
    # ... 后续代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 08:47:04