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

Flask蓝图中@login_required导致JS Fetch请求返回401问题

问题:Flask前后端分离下@login_required拦截已登录请求(Session ID不一致)

问题详情

采用前后端分离架构,后端基于Flask开发,前端通过JS的fetch请求与后端交互。后端使用Flask-Login实现认证,部分接口通过@login_required装饰器保护。

已成功登录(返回200状态码),但通过JS调用受保护的fund_request_bp蓝图接口时,@login_required触发401未授权错误。经排查发现:

  • 登录后的Session ID与fund_request_bp蓝图接口获取到的Session ID不一致
  • fund_request_bp蓝图下所有接口的Session ID均相同
  • Postman测试无此问题

期望请求能正常进入受保护接口,不被@login_required拦截。

相关代码

登录接口代码

from config import app, db, login_manager, default_password
from models import User
from flask import Blueprint, request, jsonify, session
from flask_login import login_user, login_required, current_user

user_bp = Blueprint("user", __name__)

@user_bp.route('/login', methods=['POST'])
def login():
    data = request.form

    user = User.query.filter_by(username=data.get('username')).first()

    if (user and user.check_password(data.get('password')) and data.get('password')==default_password):
        return jsonify({"message": "Redirect To Change Password !"}), 303
    elif (user and user.check_password(data.get('password'))):
        login_user(user)
        session['test'] = 'valuetest'
        print(session.sid)
        return jsonify({"message": "Login successfully"}), 200
    else:
        return jsonify({"error": "Invalid username or password."}), 404

受保护蓝图接口代码

fund_request_bp = Blueprint("fund_request", __name__, url_prefix="/fund_requests")

@fund_request_bp.route("/", methods=["GET"])
@login_required
def get_all_fund_request():
    fund_requests = FundRequest.query.filter_by(approval_level=current_user.user_level.level)

    if fund_requests.first() is None:
        return jsonify({"message": "No Funds request found."}), 200

    fund_requests_serialized = [fund_request.serialize() for fund_request in fund_requests]

    return jsonify(fund_requests_serialized), 200

前端Fetch请求代码

fetch(api_base+"/fund_requests", {
    credentials: 'include'
})
.then(response => response.json())
.then(data => {
    console.log(data)
})
.catch(error => {
    console.error("Error fetching data:", error);
});

Flask配置代码

app = Flask(__name__)
app.config['SECRET_KEY'] = 'some_secret_key'
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_COOKIE_PATH'] = '/'
login_manager = LoginManager(app)
CORS(app, supports_credentials=True)
Session(app)

解决方案

1. 完善CORS配置,指定允许的前端源

前后端分离场景下,浏览器会严格校验跨域请求的来源,仅开启supports_credentials=True不够,需要明确指定前端的域名(开发环境可临时用"*",生产环境必须指定具体域名):

# 替换成你的前端实际地址,比如http://localhost:3000
CORS(app, supports_credentials=True, origins=["http://your-frontend-domain.com"])

2. 调整Session Cookie的SameSite属性

跨域场景下,默认的SameSite=Lax会阻止浏览器携带Session Cookie,需修改为SameSite=None,同时根据环境设置Secure属性:

# 开发环境(HTTP)可设为False,生产环境(HTTPS)必须设为True
app.config['SESSION_COOKIE_SECURE'] = False
app.config['SESSION_COOKIE_SAMESITE'] = 'None'

3. 确保登录请求也携带Credentials

前端登录的Fetch请求同样需要添加credentials: 'include',否则登录时的Session Cookie不会被浏览器保存:

// 示例登录请求代码
fetch(api_base+"/user/login", {
    method: 'POST',
    credentials: 'include',
    body: new FormData(document.getElementById('login-form'))
})
.then(response => response.json())
.then(data => {
    console.log(data);
    // 登录成功后再发起受保护接口请求
})
.catch(error => {
    console.error("Login error:", error);
});

4. 检查蓝图注册顺序

确保所有蓝图在Flask-Session初始化之后注册到app,避免Session上下文异常:

# 先初始化Session
Session(app)
# 再注册蓝图
app.register_blueprint(user_bp)
app.register_blueprint(fund_request_bp)

5. 验证Session一致性

在受保护接口中打印Session ID,确认是否与登录时的ID一致,辅助排查:

@fund_request_bp.route("/", methods=["GET"])
@login_required
def get_all_fund_request():
    print("Fund Request Session ID:", session.sid)
    # 剩余业务代码...

排查思路总结

Postman测试正常但前端异常,说明问题出在浏览器的跨域Cookie策略上:

  • 浏览器会对跨域请求的Cookie做严格校验,Postman不受此限制
  • 核心是确保Session Cookie能被浏览器正确携带到跨域请求中,需从CORS配置、Cookie属性、请求Credentials设置三个维度入手

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 13:05:18