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

Flask-login未调用load_user,用户认证状态无法跨请求持久化

问题描述

向/api/login发送POST请求登录后,从未返回“用户已登录”提示;访问/api/user也无法返回已认证用户的邮箱。在load_user函数中添加打印语句后,该函数从未被调用。用户创建并提交到数据库后能成功生成ID,注册、数据库持久化、登录流程本身正常,但用户认证状态无法在请求间保持。


相关代码

init.py

# standard library imports

# third-party imports
from flask import Flask 

from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from flask_login import LoginManager

# local
from site_t.config import Config


app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
CORS(app, supports_credentials=True)
login = LoginManager(app)
login.session_protection = "strong"


@app.before_first_request
def create_tables():
    db.create_all()


from site_t import routes, models

models.py

from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash

from site_t import db, login

@login.user_loader
def load_user(id):
    return User.query.get(int(id))

class User(db.Model, UserMixin): 
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))

    def __repr__(self):
        return '<User {}>'.format(self.email)

    def set_password(self, password): 
        self.password_hash = generate_password_hash(password) 

    def check_password(self, password): 
        return check_password_hash(self.password_hash, password)

Routes.py

# third party imports
from flask import request, jsonify
from flask_login import login_user, current_user, login_required


# local imports
from site_t import app, db 
from site_t.models import User
from site_t.communication.receiver import parse_login_request, parse_registration_request


# NOTE: This route is needed for the default EB health check route
@app.route('/')
def home():
    return jsonify({'message': 'Welcome to the API'})

@app.route("/api/login", methods=["POST"])   # type: ignore
def login():
    if current_user.is_authenticated:
        return jsonify({"message": "User already logged in"}), 200

    try:
        email, password = parse_login_request(request)
    except ValueError as e:
        return jsonify({"message": str(e)}), 400

    user = User.query.filter_by(email=email).first()
    

    if user is None or not user.check_password(password):
        return jsonify({"message": "Invalid email or password"}), 200

    login_user(user, remember=True)

    return jsonify({"message": "Login successful"}), 200



def create_user(email, password):
    user = User(email=email)
    user.set_password(password)
    return user

@app.route("/api/register", methods=["GET", "POST"])   # type: ignore
def register():
    if current_user.is_authenticated:
        return jsonify({"message": "User already logged in"}), 400

    try: 
        email, password = parse_registration_request(request)
    except ValueError as e:
        return jsonify({"message": str(e)}), 400
    
    if User.query.filter_by(email=email).first() is not None:
        return jsonify({"message": "Email already registered"}), 200

    user = create_user(email, password) 


    db.session.add(user)
    db.session.commit()

    return jsonify({"message": "Registration successful"}), 200


@app.route("/api/user", methods=["GET, POST"])   # type: ignore
@login_required
def get_user():
    if not current_user.is_authenticated:
        return jsonify({"message": "User not logged in"}), 400

    return jsonify({"email": current_user.email})

wsgi.py

from site_t import app

if __name__ == '__main__':
    app.run()

问题排查与修复方案

1. 缺失会话密钥配置

Flask-Login依赖Flask的会话机制存储用户认证状态,必须配置SECRET_KEY才能正常加密会话数据,否则会话无法在请求间持久化。

  • 修复方法:在Config类中添加SECRET_KEY,生产环境使用随机生成的安全密钥:
    # config.py
    import os
    class Config:
        SECRET_KEY = os.urandom(24)  # 生产环境用随机生成的密钥
        # 其他已有配置...
    

2. 路由请求方法配置错误

/api/user路由的methods参数写法错误,"GET, POST"是单个字符串,Flask无法识别正确的请求方法,导致login_required装饰器无法触发会话校验。

  • 修复方法:修改为正确的列表格式:
    @app.route("/api/user", methods=["GET", "POST"])  # 逗号分隔每个方法,各自用引号包裹
    @login_required
    def get_user():
        # 原代码逻辑不变
    

3. 跨域请求凭证携带问题

即使后端设置了CORS(app, supports_credentials=True),前端请求时也必须显式携带凭证(Cookie),否则会话无法在跨域请求间保持。

  • 前端修复示例:
    • Axios:
      axios.get('/api/user', { withCredentials: true })
      
    • Fetch:
      fetch('/api/user', { credentials: 'include' })
      

4. 会话Cookie安全配置(生产环境必备)

部署到生产环境时,需配置合适的Cookie属性避免会话劫持:

# 在Config类中添加
SESSION_COOKIE_SECURE = True  # 仅HTTPS下传输Cookie
SESSION_COOKIE_HTTPONLY = True  # 禁止JS访问Cookie,防止XSS攻击
SESSION_COOKIE_SAMESITE = "Lax"  # 限制跨站Cookie发送,根据跨域需求调整为"Strict"或"None"

5. 验证load_user注册顺序

确保login对象初始化完成后再导入models模块(因为models中的@login.user_loader需要绑定到已初始化的login实例),当前init.py的导入顺序是正确的(先初始化login,再导入routes和models),若存在循环导入问题,可调整导入位置。


验证步骤

  1. 启动服务后,先注册用户,确认数据库中生成用户记录。
  2. 发送登录请求,确保返回Login successful。
  3. 携带凭证访问/api/user,检查是否返回用户邮箱。
  4. 查看load_user函数的打印输出,确认该函数被调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 12:33:19