我的 Flask 应用程序使用 JWT 作为身份验证手段。这些令牌存储在 cookie 中,并且 Flask-jwt-extended 配置为使用它们。对于常规 GET 请求,身份验证工作正常,并且
@jwt_required
装饰器能够从 cookie 中读取令牌并对用户进行身份验证。但是,当使用 fetch()
发出 AJAX POST 请求时,扩展程序无法读取它们并返回 Missing CSRF token
错误。奇怪的是,当在 POST 路由中访问请求对象时,所有必需的 cookie 都存在,并且在经过身份验证时在所有其他路由中都存在,这意味着 fetch()
正确设置了所有必需的 cookie:
ImmutableMultiDict([
('csrftoken', 'valid_csrf_token'),
('session','valid_session_cookie'),
('access_token_cookie', 'valid_access_token'),
('csrf_access_token', 'valid_csrf_access_token')
])
Flask POST 路线:
@main.route("/sendmail", methods=["POST"])
@jwt_required()
async def send_mail():
data = json.loads(request.data)
mail_template = render_template("mail-view.html", data=data)
pdf_report = pdfkit.from_string(mail_template, False)
message = Message(
subject="Flask-Mailing module",
recipients=["[email protected]"],
body="Message body",
subtype="html",
)
message.attach("report.pdf", pdf_report)
await mail.send_message(message)
return jsonify({"message": "success"}), 200
获取请求:
fetch(window.location.origin + "/sendmail", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "same-origin",
body: JSON.stringify(mail),
})
我的应用程序配置对象:
class DevConfig:
SECRET_KEY = os.environ.get("SECRET_KEY")
JWT_SECRET_KEY = os.environ.get("JWT_SECRET_KEY")
JWT_COOKIE_SECURE = False
SESSION_COOKIE_SECURE = False
JWT_TOKEN_LOCATION = ["cookies"]
JWT_ACCESS_TOKEN_EXPIRES = datetime.timedelta(hours=1)
MAIL_SERVER = "smtp.googlemail.com"
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USE_SSL = False
MAIL_USERNAME = os.environ.get("MAIL_USERNAME")
MAIL_PASSWORD = os.environ.get("MAIL_PASSWORD")
MAIL_FROM = os.environ.get("MAIL_USERNAME")
使用flask-jwt-extended默认设置,仅需要为状态更改请求方法(除“GET”之外的所有方法)发送CSRF-Token。这就是为什么即使没有 CSRF 令牌,“GET”方法也会被授权。其次,CSRF-Token 的整体思想是网络浏览器不会自动发送它,因此它不会在 cookie 中被接受:
默认情况下,我们通过在有人访问时设置两个 cookie 来实现此目的 登录。第一个 cookie 包含 JWT,并在该 JWT 中进行编码 是双重提交令牌。此 cookie 设置为仅限 http,因此 它不能通过 javascript 访问(这就是防止 XSS 攻击的原因) 以免能够窃取 JWT)。我们设置的第二个 cookie 包含 只有相同的双重提交令牌,但这次是在 cookie 中 可由 JavaScript 读取。每当提出请求时,都需要 包含 X-CSRF-TOKEN 标头,以及双重提交的值 令牌。如果此标头中的值与存储在中的值不匹配 JWT,该请求被视为无效而被踢出。
https://flask-jwt-extend.readthedocs.io/en/stable/token_locations/
因此,每当您发送请求时,您都应该将 CSRF-Token 添加到标头中:
fetch(window.location.origin + "/sendmail", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": getCookie("csrf_access_token"),
},
credentials: "same-origin",
body: JSON.stringify(mail),
})
抱歉我来晚了。无论如何,这对于
Flask Template
形式可能有用。
missing csrf access token
的主要问题可能是由于 .html
csrf 上的表单元素未设置而发生。为了修复可以按照以下步骤操作:
1。在表单 python 上添加表单验证器(link):
表单.py
from wtforms import Form, BooleanField, StringField, PasswordField, validators
class LoginForm(Form):
username = StringField('Username', [validators.DataRequired()])
password = PasswordField('New Password', [validators.DataRequired()])
app.py
from flask_restful import Api, Resource, reqparse, marshal, request
from forms import LoginForm
@app.route('/login/', methods=['GET', 'POST'])
def login():
form = LoginForm(request.form)
if request.method == 'POST' and form.validate():
....
return render_template('login.html', title='Login', form=form)
2。然后使用 Flask CSRF (link) 作为示例:
<form method="post">
{{ form.csrf_token }}
{{ form.hidden_tag() }}
<div>{{ form.library.label }}: {{ form.library() }}</div>
<div>{{ form.books.label }}: {{ form.books() }}</div>
<div>{{ form.submit.label }}: {{ form.submit() }}</div>
</form>
样品: Flask wtforms - “UnboundField”对象不可调用,动态字段无法正确初始化
Flask 模板表单需要使用表单验证器和 CSRF 令牌才能使其工作