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

Flask+SQLAlchemy表单开发遇CSRF Token过期错误求助

Flask表单CSRF令牌过期问题排查与解决

问题背景

开发基于Flask的Web表单并与SQLAlchemy(SQLite)交互时,已配置SECRET_KEY和CSRFProtect,但提交表单仍出现「Bad Request : The CSRF token has expired.」错误。以下是原代码:

原后端代码

#!/usr/bin/python
from sqlite3 import connect
from flask import Flask,  render_template, redirect
from flask_wtf import FlaskForm
from wtforms import *
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from flask_wtf.csrf import CSRFProtect
from sqlalchemy.orm import sessionmaker

engine = create_engine("sqlite:///test2.db", echo=True)
Session = sessionmaker(bind=engine)
conn = engine.connect()
session = Session(bind=conn)
app = Flask(__name__)
csrf = CSRFProtect(app)
csrf.init_app(app)
app.config['SECRET_KEY'] = b'zyzz'
db = declarative_base()

class Todo(db):

    __tablename__ = 'test'
    login =Column(String, primary_key=True)
    name = Column(String(20))
    lastname =Column(String(20))
    desc = Column(String(200))

    def __init__(self, login, name, lastname, desc):
        self.login = login
        self.name = name
        self.lastname = lastname
        self.desc = desc

db.metadata.create_all(engine)

class testForm(FlaskForm):
        name = StringField("name")
        lastname = StringField("lastname")
        login = StringField("login")
        desc = StringField("desc")
        submit = StringField("submit")

@app.route("/")
def hello(): 
    return "yo"

@app.route("/add_user", methods=['GET', 'POST'])
def submit():
    form = testForm()
    if form.validate_on_submit():
        session.add(form)   
        session.commit()
        return redirect('/success')    
    return render_template('form.html', form = form)

@app.route("/success")
def success(): 
    return "success"

if __name__ == '__main__':
    app.run(debug = True, port=5000)

原前端代码

<html>
<body>
<form method="post" name ="form" id="form">
{{ form.csrf_token }}
<input type=text id=name> Name</input><br>
<input type=text id=lastname > LastName </input><br>
<input type=text id=login /> Login </input><br>
<input type=text id=desc /> Description </input><br>
<input type=submit id=submit/>
</form>
</body> 
</html>

错误原因分析

  • 前端表单字段未正确绑定:前端input仅设置id,未添加name属性,也未使用FlaskForm的字段渲染方法,导致后端无法识别表单数据,触发CSRF验证逻辑异常。
  • CSRFProtect初始化顺序错误:先实例化CSRFProtect(app)再配置SECRET_KEY,导致CSRFProtect初始化时未读取到有效密钥,令牌生成与验证逻辑失效。
  • SQLAlchemy会话操作错误:直接将FlaskForm实例添加到数据库会话,这是错误的,必须创建Todo模型实例并赋值表单数据后再添加。
  • SECRET_KEY格式问题:使用字节串b'zyzz'作为密钥,虽Flask支持,但可能导致会话加密不稳定,引发令牌过期假象。

解决方案

后端代码修正

#!/usr/bin/python
from flask import Flask, render_template, redirect
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from sqlalchemy import create_engine, String, Column
from sqlalchemy.ext.declarative import declarative_base
from flask_wtf.csrf import CSRFProtect
from sqlalchemy.orm import sessionmaker

# 数据库初始化
engine = create_engine("sqlite:///test2.db", echo=True)
Session = sessionmaker(bind=engine)
session = Session()
db = declarative_base()

# Flask应用初始化 - 先配置SECRET_KEY再初始化CSRFProtect
app = Flask(__name__)
app.config['SECRET_KEY'] = 'zyzz'  # 改用字符串格式
csrf = CSRFProtect(app)

class Todo(db):
    __tablename__ = 'test'
    login = Column(String, primary_key=True)
    name = Column(String(20))
    lastname = Column(String(20))
    desc = Column(String(200))

    def __init__(self, login, name, lastname, desc):
        self.login = login
        self.name = name
        self.lastname = lastname
        self.desc = desc

db.metadata.create_all(engine)

class testForm(FlaskForm):
    name = StringField("name")
    lastname = StringField("lastname")
    login = StringField("login")
    desc = StringField("desc")
    submit = SubmitField("submit")  # 改用SubmitField而非StringField

@app.route("/")
def hello(): 
    return "yo"

@app.route("/add_user", methods=['GET', 'POST'])
def submit():
    form = testForm()
    if form.validate_on_submit():
        # 创建Todo模型实例,赋值表单数据
        new_user = Todo(
            login=form.login.data,
            name=form.name.data,
            lastname=form.lastname.data,
            desc=form.desc.data
        )
        session.add(new_user)
        session.commit()
        return redirect('/success')    
    return render_template('form.html', form=form)

@app.route("/success")
def success(): 
    return "success"

if __name__ == '__main__':
    app.run(debug=True, port=5000)

前端代码修正

<html>
<body>
<form method="post" name="form" id="form">
    {{ form.csrf_token }}
    <!-- 使用FlaskForm字段渲染,自动生成正确的name属性 -->
    {{ form.name.label }} {{ form.name() }}<br>
    {{ form.lastname.label }} {{ form.lastname() }}<br>
    {{ form.login.label }} {{ form.login() }}<br>
    {{ form.desc.label }} {{ form.desc() }}<br>
    {{ form.submit() }}
</form>
</body> 
</html>

关键修正点说明

  1. 调整CSRF初始化顺序:先配置SECRET_KEY再初始化CSRFProtect,确保密钥被正确加载。
  2. 表单字段绑定:前端使用FlaskForm的字段渲染方法,自动生成匹配后端表单字段的name属性,保证数据能正确提交。
  3. 数据库会话修正:创建Todo模型实例,将表单数据赋值给模型字段后再添加到会话,避免直接添加表单对象导致的错误。
  4. 表单字段类型修正:将提交按钮改为SubmitField,符合WTForms的规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 16:21:12