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
相关产品推荐
相关产品推荐

