Flask启用CSRFProtect后Axios请求报CSRF会话令牌缺失错误排查
解决跨服务调用Flask CSRF保护接口的400错误问题
问题根源
你遇到的CSRF session token missing错误,核心原因有两个:
- Flask端手动硬写
session['csrf_token']不符合flask-wtf的CSRF token生成规范,框架无法识别这个值; - 跨域请求下,Flask的session Cookie没有正确传递到后端,导致session为空,CSRF验证环节找不到对应token。
具体修复步骤
1. 修正Flask后端的CSRF配置与token生成逻辑
移除硬写token的before_request钩子,改用flask-wtf提供的标准方法生成和传递CSRF token:
'''Using flask to create the python ws''' from flask import Flask, request, session, make_response from flask_cors import CORS from flask_wtf.csrf import CSRFProtect, generate_csrf from dotenv import dotenv_values from Validator import geia_std_xsd_validation config = dotenv_values('.env') ALLOWED_EXTENSIONS = {'xml'} APP = Flask(__name__) SECRET_KEY = 'test12' csrf = CSRFProtect() origin_array = config['WHITELIST_ARRAY'] # 调整CORS配置,确保跨域Cookie能正常传递 cors = CORS(APP, origins=origin_array, expose_headers=['X-CSRFToken'], supports_credentials=True) csrf.init_app(APP) APP.config['SECRET_KEY'] = SECRET_KEY # 跨域场景下的Cookie配置(根据实际环境调整) APP.config['SESSION_COOKIE_SAMESITE'] = 'None' # 跨不同域名时设置为None APP.config['SESSION_COOKIE_SECURE'] = True # HTTPS环境下开启,确保Cookie只通过HTTPS传递 APP.vars = {} def allowed_file(filename): return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @APP.route('/', methods = ['GET']) def server_up(): # 生成标准CSRF token,并设置到响应头 csrf_token = generate_csrf() response = make_response({ "message": "Server is up", "csrf_token": csrf_token }) # 把token放到响应头,方便前端获取 response.headers['X-CSRFToken'] = csrf_token return response @APP.route('/xsd_validator', methods = ['POST']) def xsd_validation(): if request.method == 'POST': print('HITTING') if len(request.files) <= 0: print('No files in response') return 'No file sent' file = request.files['testFile'] if file.filename == '': print('Blank filename') return 'Invalid filename' if file and allowed_file(file.filename): print('Got into the file statement') file_text = file.read() errors = geia_std_xsd_validation(file_text) return errors return 'Nothing Happened' if __name__ == "__main__": APP.run(ssl_context='adhoc', port=6030) # 测试时用adhoc生成临时HTTPS证书,正式环境用合法证书
2. 调整Express端Axios请求逻辑
不要硬写CSRF token,先从Flask的GET接口获取有效token,再发起POST请求;同时不要手动设置multipart/form-data头,避免丢失表单边界:
const testTHATSHIZZLE = async (req, res) => { try { // 第一步:获取Flask生成的有效CSRF token const csrfRes = await axios.get('https://localhost:6030/', { withCredentials: true // 必须开启,才能接收Flask的session Cookie }); const csrfToken = csrfRes.data.csrf_token; // 第二步:构造文件请求 const file = req.files.testFile const submittedFile = new FormData() submittedFile.append('testFile', file.data, file.name) // 发起POST请求 const response = await axios.post('https://localhost:6030/xsd_validator', submittedFile, { withCredentials: true, headers: { 'X-CSRFToken': csrfToken // 带上获取到的有效token // 不要手动设置Content-Type,FormData会自动生成带boundary的正确头 } }) res.send(response.data) } catch (error) { console.log(error.response?.data || error.message) res.status(error.response?.status || 500).send(error.response?.data || 'Request failed') } }
关键说明
- Session Cookie传递:跨域下必须开启
withCredentials: true,同时Flask端要设置SESSION_COOKIE_SAMESITE='None'和SESSION_COOKIE_SECURE=True(HTTPS环境),否则浏览器会阻止Cookie跨域传递,导致Flask端session为空。 - CSRF Token规范:flask-wtf的CSRF验证依赖框架生成的token,手动写入session的
csrf_token不会被框架识别,必须用generate_csrf()生成标准token。 - Content-Type设置:发送FormData时,不要手动设置
multipart/form-data,Axios会自动添加正确的boundary参数,否则后端无法解析表单数据。
内容的提问来源于stack exchange,提问作者Thome
相关产品推荐
相关产品推荐

