如何在具有多个蓝图的烧瓶中处理登录?
Posted
技术标签:
【中文标题】如何在具有多个蓝图的烧瓶中处理登录?【英文标题】:How do I handle login in flask with multiple blueprints? 【发布时间】:2013-12-06 19:34:27 【问题描述】:我有多个蓝图需要集成到一个应用中。我正在使用flask-login
来处理登录。但是,我对如何为我的蓝图处理 LoginManager()
和 .user_loader
感到困惑。
这是我当前的文件结构。
system/
run.py
config.py
app/
__init__.py
models.py
views/
blueprint1.py
blueprint2.py
static/
templates/
<templates>
实施它们的正确方法是什么?我只是在__init__.py
上调用它们并在蓝图中导入登录管理器变量吗?还是我需要在蓝图中单独调用它们?
希望我能够清楚地描述这个问题。感谢您的阅读和回答
【问题讨论】:
【参考方案1】:您必须了解,对于一个应用程序,无论您使用多少个蓝图,您都必须使用一个登录管理器(当然,可能有特定的例外情况,例如当蓝图独立时,但在这种情况下您可能无法使用@987654321 @)。因为:
-
您有 1 个入口点
如果用户没有登录,他将被重定向到登录/注册页面
您有 1 个用户加载器
登录管理器的工作原理:
-
它在请求上下文中注册
current_user
before_request
读取您的会话,获取用户 ID,使用 user_loader
加载用户并将其设置为 current_user
或 AnonymousUser
当您访问私人页面时,login_required
会检查 current_user.is_authenticated()
否则会重定向到登录页面
登录时,它将用户 ID 添加到会话中
因此,您必须只为烧瓶应用程序初始化一个登录管理器实例,然后在所有蓝图中使用login_required
和current_user
。
【讨论】:
简洁,正是我所需要的。非常感谢。 这意味着我不能将 login_manager 用于多个实例?喜欢管理页面和用户页面?【参考方案2】:这就是我的处理方式:
这是我初始化所有内容的地方:
import logging
import logging.config
import flask
import flask.globals as flask_global
import flask_login
from config import flask as flask_config
from rest.api import dashboard
from rest.api.util import login_decorator
logger = logging.getLogger(__name__)
# app
flask_app = flask.Flask(__name__)
flask_app.config.from_object(flask_config)
# login manager needs be set before blueprint registration
login_manager = flask_login.LoginManager()
login_manager.init_app(flask_app)
flask_app.register_blueprint(dashboard.blueprint)
# setting blueprint specific login view
# login_manager.login_view = "login"
@login_manager.user_loader
def load_user(user_id):
"""
This will be used many times like on using current_user
:param user_id: username
:return: user or none
"""
# http://librelist.com/browser/flask/2012/4/7/current-blueprint/#44814417e8289f5f5bb9683d416ee1ee
blueprint = flask_global.current_app.blueprints[request.blueprint]
if hasattr(blueprint, load_user):
return blueprint.load_user(user_id)
# https://flask-login.readthedocs.org/en/latest/#how-it-works
return None
这是我自己处理登录的蓝图:
from __future__ import absolute_import
import flask
import flask_login
from flask import Blueprint
from core.models.profile import Agent
from core.utils import thread_local
from rest.api.util import login_decorator
blueprint = Blueprint('human', __name__, url_prefix='/human')
def load_user(user_id):
"""
This will be used many times like on using current_user
:param user_id: username
:return: user or none
"""
agent = None
try:
agent = Agent.objects.get(username=user_id)
except:
# https://flask-login.readthedocs.org/en/latest/#how-it-works
pass
return agent
@blueprint.record_once
def on_load(state):
"""
http://***.com/a/20172064/742173
:param state: state
"""
blueprint.load_user = load_user
state.app.login_manager.blueprint_login_views[blueprint.name] = 'human.login'
@blueprint.route('/login', methods=['POST'])
@login_decorator.login_not_required
def login():
username = flask.request.args.get('username')
password = flask.request.args.get('password')
try:
agent = Agent.objects.get(username=username)
except:
return 'Invalid username'
if not agent.check_password(password):
return 'Invalid password'
flask_login.login_user(agent)
return 'Valid login'
@blueprint.route("/logout")
def logout():
flask_login.logout_user()
return 'Logout done'
@blueprint.before_request
def before_request():
agent = flask_login.current_user
# https://flask-login.readthedocs.org/en/latest/#anonymous-users
is_logged_in = agent.get_id() is not None
login_not_required = getattr(flask.current_app.view_functions[flask.request.endpoint], 'login_not_required', False)
is_static_resource_call = flask.request.endpoint.startswith('static/')
if is_static_resource_call or is_logged_in or login_not_required:
if is_logged_in:
thread_local.set_current_brand_id(agent.brand_id)
else:
flask.abort(401)
# if we want to redirect to some page then we can use this. The appropriate login_view should be set
# return flask.current_app.login_manager.unauthorized()
希望对你有帮助。
【讨论】:
【参考方案3】:如果由于文档不清晰而仍然有人面临这个挑战,这里有一个解决方案
在您的情况下,您需要将登录管理器声明与烧瓶应用程序实例放在同一文件中。这通常是带有app = Flask(__name__).
的__init__.py
文件
在顶部,导入 LoginManager 类
from flask_login import LoginManager
然后将其绑定到应用实例。
login_manager = LoginManager()
login_manager.init_app(app)
(这不是被问到的,只是以防有人需要)假设您有管理员和普通用户,并且您正在从不同的表进行身份验证:
@login_manager.user_loader
def load_user(user_id):
x = Users.query.get(str(user_id))
if x == None:
x = Admins.query.get(str(user_id))
return x
最后,在导入蓝图后,您可以在字典中定义每个登录视图
login_manager.blueprint_login_views =
'admin': '/admin/login',
'site': '/login',
由于您将登录管理器绑定到烧瓶应用程序实例,因此无需将其导入任何蓝图
【讨论】:
请注意,query.get() 与“where primary_key_field = user_id”相同,因此如果您拥有 >= 与管理员一样的用户数,那么这将永远不会搜索 admin 表,假设两者都是自动递增的 int PKs 好点。从来没想过,因为我所有的主键通常都是 uuid4()。 嗨,我在这里假设您有不同的站点和管理员登录名。如果我只有一种类型的用户(和多个蓝图)怎么办?这种方法行得通吗? 是的,它应该可以工作。您只需要删除查询 admin 表的 if 语句。以上是关于如何在具有多个蓝图的烧瓶中处理登录?的主要内容,如果未能解决你的问题,请参考以下文章