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

Flask-login开发环境正常生产环境登录后API返回401问题

Flask登录后API返回401未授权问题排查

预期行为

  • 用户登录后重定向至首页,从API加载并持久化用户信息

报错情况

  • 首页请求API时返回401未授权

我用Flask开发大型应用,搭配Flask-login、SQLAlchemy和MySQL,数据库部署在Avian,后端托管于Heroku,前端React应用部署在Vercel。开发环境测试一切正常,但部署到Heroku(也尝试过PythonAnywhere)后出现问题:用户登录接口返回200成功,但重定向到首页后,调用API返回401提示未登录。

已尝试的解决方案

  • 使用SQLAlchemy类型会话持久化会话状态
  • 添加会话Cookie域名配置
  • 集成Flask-session
  • 引入Flask-talisman强制HTTPS
  • 检查CORS设置,确保包含所有域名
  • 确认user_loader配置正常
  • 在Procfile中使用web: gunicorn app:app --preload命令

额外配置信息

Python 3.8,Flask 3.0.2

Heroku日志

2024-03-15T05:51:18.006552+00:00 app[web.1]: 10.1.26.237 - - [15/Mar/2024:05:51:17 +0000] "OPTIONS /api/users/login HTTP/1.1" 200 0 "<my url>/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
2024-03-15T05:51:18.006707+00:00 heroku[router]: at=info method=OPTIONS path="/api/users/login" host=<heroku project>.herokuapp.com request_id=575d6668-8725-428c-b338-8fc83134e4bf fwd="60.254.90.89" dyno=web.1 connect=0ms service=2ms status=200 bytes=679 protocol=https
2024-03-15T05:51:19.955045+00:00 app[web.1]: 10.1.26.237 - - [15/Mar/2024:05:51:19 +0000] "POST /api/users/login HTTP/1.1" 200 38 "<my url>/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
2024-03-15T05:51:19.955217+00:00 heroku[router]: at=info method=POST path="/api/users/login" host=<heroku project>.herokuapp.com request_id=85ae383d-020b-40b5-94fd-968652d892d4 fwd="60.254.90.89" dyno=web.1 connect=0ms service=1627ms status=200 bytes=961 protocol=https
2024-03-15T05:51:21.549672+00:00 app[web.1]: 10.1.26.237 - - [15/Mar/2024:05:51:21 +0000] "OPTIONS /api/users/status HTTP/1.1" 200 0 "<my url>/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
2024-03-15T05:51:21.549804+00:00 heroku[router]: at=info method=OPTIONS path="/api/users/status" host=<heroku project>.herokuapp.com request_id=722dfc4b-e1df-4796-90c0-55ec73788ee3 fwd="60.254.90.89" dyno=web.1 connect=0ms service=2ms status=200 bytes=673 protocol=https
2024-03-15T05:51:21.871561+00:00 heroku[router]: at=info method=GET path="/api/users/status" host=<heroku>.herokuapp.com request_id=5e32fc4b-3011-4982-ae4d-bc4baf91b7b9 fwd="60.254.90.89" dyno=web.1 connect=0ms service=2ms status=401 bytes=569 protocol=https

精简代码

create_app.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from flask_session import Session
import os
from flask_talisman import Talisman

# 初始化SQLAlchemy数据库
db = SQLAlchemy()

# 创建Flask应用
def create_app():
    app = Flask(
        __name__,
        static_url_path="",
        static_folder="../client/build",
        template_folder="build",
    )  # 初始化Flask应用

    CORS(app, origins=["<localhost>", "<my url>", "<my url with www>", "<vercel>", "<vercel1>", "<vercel2>"])

    app.config[
        "SQLALCHEMY_DATABASE_URI"
    ] = "mysql+pymysql://<avn database url>"
    app.config["SECRET_KEY"] = os.getenv("SECRET_KEY")  # Flask应用密钥
    app.config["UPLOAD_FOLDER"] = "static/images"
    app.config['CORS_LOGGING'] = True
    db.init_app(app)
    app.config["SESSION_TYPE"] = "sqlalchemy"
    app.config["SESSION_SQLALCHEMY"] = db  # 已创建的SQLAlchemy实例
    Session(app)
    app.config['SESSION_COOKIE_DOMAIN'] = '.<my url>'

    return app


# 初始化应用供其他文件使用
app = create_app()

Talisman(app)

app.py

from flask_login import LoginManager
from server_module.create_app import app
from server_module.models import User

# 配置登录管理器
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "loginUser"


# 用户加载器
@login_manager.user_loader
def load_user(id):
    user = User.query.get(id)
    return user


# 未授权访问处理
@login_manager.unauthorized_handler
def unauthorized_handler():
    return "Unauthorized", 401


# 注册用户加载器
login_manager.user_loader(load_user)

models.py

import uuid
from sqlalchemy.sql import func
from werkzeug.security import generate_password_hash
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
from sqlalchemy_serializer import SerializerMixin
from ..create_app import db

class User(db.Model, UserMixin, SerializerMixin):
    # 用户表字段定义
    id = db.Column(
        db.String(40),
        primary_key=True,
        nullable=False,
        default=lambda: str(uuid.uuid4()),
    )
    email = db.Column(db.String(150), nullable=False, unique=True)
    password = db.Column(db.String(500), nullable=False)
    
    def __init__(self, email, password):
        self.email = email
        self.password = generate_password_hash(password=password)

users.py

from flask import Blueprint, request
from flask_login import login_user, current_user
from ..models import User
from werkzeug.security import check_password_hash
from ..utility import error_msg

users_blueprint = Blueprint("users", __name__)

# 登录API
@users_blueprint.route("/login", methods=["POST"])  # 已完成
def loginUser():
    try:
        try:
            data = request.get_json()
        except:
            return error_msg()

        if not all(key in data for key in ("password", "email")):
            return error_msg()

        user = User.query.filter_by(email=data["email"]).first()
        if not user:
            return error_msg(404)

        if not check_password_hash(user.password, data["password"]):
            return {"msg": "密码错误"}, 400

        login_user(user, remember=True)  # 用户登录

        return {"msg": "用户登录成功"}, 200
    except Exception as e:
        print("发生错误: ", e)
        return error_msg(500, e)


# 用户状态API,用于导航栏获取用户状态
@users_blueprint.route("/status", methods=["GET"])  # 已完成
def getUserStatus():
    try:
        if current_user.is_authenticated:
            obj = current_user.to_dict(
                only=(
                    "username",
                    "type",
                    "status",
                    "avatar",
                    "id"
                )
            )
            return {"msg": obj}, 200
        else:
            return error_msg(401)
    except Exception as e:
        print("发生错误: ", e)
        return error_msg(500, e)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 18:59:56