如何处理具有多个蓝图的 Flask 登录?

问题描述 投票:0回答:5

我有多个蓝图需要集成到一个应用程序中。我正在使用

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
处调用它们并在蓝图上导入登录管理器变量?或者我需要在蓝图中单独调用它们吗?

希望我能够清楚地描述这个问题。感谢您的阅读和回答

python flask-login flask
5个回答
14
投票

您必须明白,对于一个应用程序,无论您使用多少个蓝图,您都必须使用一个登录管理器(当然可能有特定的例外,例如当蓝图独立时,但在这种情况下您可能无法使用

flask-login
) 。因为:

  1. 您有 1 个入口点
  2. 如果用户未登录,他将被重定向到登录/注册页面
  3. 您有 1 个用户加载程序

登录管理器的工作原理:

  1. 它在请求上下文中注册
    current_user
  2. before_request
    读取您的会话,获取用户 ID,使用
    user_loader
    加载用户并将其设置为
    current_user
    AnonymousUser
  3. 当您访问私人页面时,
    login_required
    会检查
    current_user.is_authenticated()
    ,否则会重定向到登录页面
  4. 登录时,它将用户 ID 添加到会话中

因此,您必须为 Flask 应用程序初始化一个登录管理器实例,然后在所有蓝图中使用

login_required
current_user


7
投票

我就是这样处理的:

这是我初始化一切的地方:

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://stackoverflow.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()

希望有帮助。


4
投票

如果由于文档不那么清晰而导致任何人仍然面临这个挑战,这里有一个解决方案

在您的情况下,您需要将登录管理器声明放置在与 Flask 应用程序实例相同的文件中。这通常是一个带有

__init__.py
app = Flask(__name__).
文件 在顶部,导入 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',
}

由于您将登录管理器绑定到 Flask 应用程序实例,因此无需将其导入任何蓝图中


2
投票

文档不清楚,我会在花了一些时间研究后写下我的想法。

只需要在flask app.py和init_app中定义一次login_manager即可。 然后在每个蓝图顶部添加

from flask_login import login_required
并像往常一样使用 @login_required。事实证明,无需声明 login_manager 即可使用。

代码示例(具有单个蓝图的应用程序)

应用程序.py

import flask
from flask import redirect, url_for
import flask_login

from blueprint.secret import secret_bp
from model.user import User

login_manager = flask_login.LoginManager()

app = flask.Flask(__name__)
app.register_blueprint(secret_bp, url_prefix="/secret")

login_manager.init_app(app)

@login_manager.user_loader
def load_user(user_id):
    return User.get(user_id)

@app.route('/login', methods=['GET', 'POST'])
def login():
    if flask.request.method == "GET":
        return "<form action='/login' method='POST'><input type='text' name='user'><button type='submit'>Submit</button></form>"

    user = flask.request.form.get('user')
    if user == "user":
        # Login and validate the user.
        # user should be an instance of your `User` class
        flask_login.login_user(user)

        flask.flash('Logged in successfully.')

        return flask.redirect(next or flask.url_for('index'))
    return flask.redirect(flask.url_for('login'))

@app.route('/admin')
def admin():
    return "Admin page"

@login_manager.unauthorized_handler
def unauthorized():
    # do stuff
    return redirect(url_for('login'))

秘密.py

from flask import Blueprint, render_template, abort
from jinja2 import TemplateNotFound
from flask_login import login_required

secret_bp = Blueprint('secret', __name__,
                        template_folder='templates')

@secret_bp.route('/noneed')
def no_need():
    return "You can see this without login."

@secret_bp.route('/needlogin')
@login_required
def show():
    return "can't see this if not login"

正如预期的那样,无需登录即可访问 /secret/noneed,并且 /secret/needlogin 将使用 @unauthorized_handler 指定的功能重定向您。


0
投票

如果你使用的是应用工厂模式,你有蓝图,你不知道把login_manager user_loader函数放在哪里,你可以把它放在create app函数中。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from module import your_blueprint

...
def create_app():
    app = Flask(__name__)
    ...
    db.init_app(app)

    login_manager = LoginManager()
    login_manager.login_view = 'auth.login'
    login_manager.init_app(app)

    from .models import User

    @login_manager.user_loader
    def load_user(user_id):
        return User.query.get(int(user_id))

    app.register_blueprint(your_blueprint, url_prefix="/home")
    ...
    return app

希望这有帮助

© www.soinside.com 2019 - 2024. All rights reserved.