How do I handle login in flask with multiple blueprints?

ぃ、小莉子 提交于 2019-11-30 07:00:59

问题


I have multiple blueprints that needs to be integrated into a single app. I'm using flask-login to handle logins. However I'm confused on how to handle the LoginManager() and the .user_loader for my blueprints.

This is my current file structure.

system/
     run.py
     config.py
     app/
        __init__.py
        models.py
        views/
             blueprint1.py
             blueprint2.py
        static/
        templates/
              <templates>

What's the correct way to implement them? Do I just call them at the __init__.py and import the login manager variable at the blueprints? or Do I need to call them individually in the blueprints?

Hopefully I'm able to portray the question clearly. Thank you for reading and answering


回答1:


You must understand that for one application you must use one login manager no matter how many blueprints you use (of course there can be specific exceptions for example when blueprints are independent, but in this case you probably can't use flask-login). Because:

  1. You have 1 entry point
  2. If user is not logged in, he will be redirected to login/registration page
  3. You have 1 user loader

How login manager works:

  1. It registers current_user in request context
  2. before_request reads your session, gets user id, loads the user with user_loader and set it to current_user or AnonymousUser
  3. When you visit the private page, login_required checks current_user.is_authenticated() else redirects to login page
  4. On login, it adds user id to the session

So you must initialize only one login manager instance for flask application and then use login_required and current_user in all your blueprints.




回答2:


This is how I have handled it:

This is where I am initialising everything:

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

Here is my blueprint with its own handling of login:

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()

Hope it helps.




回答3:


In case anyone still faces this challenge due to the documentation not being so clear, here is a solution

In your case, you need to place the login manager declaration in the same file as the flask app instance. This is commonly an __init__.py file with the app = Flask(__name__). At the top, import LoginManager class

from flask_login import LoginManager

Then tie it to the app instance.

login_manager = LoginManager()
login_manager.init_app(app)

(This was not asked but just incase someone needs it) Lets say you have admins and normal users and you are authenticating from different tables:

@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

Finally after importing blueprints you can define the login views for each in a dictionary

login_manager.blueprint_login_views = {
    'admin': '/admin/login',
    'site': '/login',
}

Since you tied the login manager to the flask app instance, there is no need of importing it into any blueprint



来源:https://stackoverflow.com/questions/20136090/how-do-i-handle-login-in-flask-with-multiple-blueprints

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!