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>
关键修正点说明
- 调整CSRF初始化顺序:先配置
SECRET_KEY再初始化CSRFProtect,确保密钥被正确加载。 - 表单字段绑定:前端使用FlaskForm的字段渲染方法,自动生成匹配后端表单字段的
name属性,保证数据能正确提交。 - 数据库会话修正:创建
Todo模型实例,将表单数据赋值给模型字段后再添加到会话,避免直接添加表单对象导致的错误。 - 表单字段类型修正:将提交按钮改为
SubmitField,符合WTForms的规范。
内容的提问来源于stack exchange,提问作者TheTricky65
相关产品推荐
相关产品推荐

