使用 flask-login 扩展

实现授权系统的一种更简单的方法是使用 flask-login 扩展。该项目的网站包含一个详细而精心编写的快速入门,本例中提供了较短版本。

大概的概念

该扩展公开了一组用于以下的函数:

  • 记录用户
  • 记录用户
  • 检查用户是否登录,并找出哪个用户

什么不做,你自己要做什么:

  • 没有提供存储用户的方法,例如在数据库中
  • 没有提供检查用户凭据的方法,例如用户名和密码

下面是一整套工作所需的最小步骤。

我建议将所有与 auth 相关的代码放在单独的模块或包中,例如 auth.py。这样,你可以单独创建必要的类,对象或自定义函数

创建一个 LoginManager

扩展使用 LoginManager 类,必须在 Flask 应用程序对象上注册。

from flask_login import LoginManager
login_manager = LoginManager()
login_manager.init_app(app) # app is a Flask object

如前所述,LoginManager 例如可以是单独文件或包中的全局变量。然后可以将其导入到创建 Flask 对象的文件中或在应用程序工厂函数中进行初始化。

指定用于加载用户的回调

通常会从数据库加载用户。回调必须返回一个对象,该对象表示与提供的 ID 相对应的用户。如果 ID 无效,则应返回 None

@login_manager.user_loader
def load_user(user_id):
    return User.get(user_id) # Fetch the user from the database

这可以在创建你的 LoginManager 之后直接完成。

代表你的用户的类

如上所述,user_loader 回调必须返回代表用户的对象。这究竟是什么意思?例如,该对象可以是存储在数据库中的用户对象的包装器,也可以是数据库中直接的模型。该对象必须实现以下方法和属性。这意味着如果回调返回数据库模型,则需要确保将所提及的属性和方法添加到模型中。

  • is_authenticated

    如果用户已通过身份验证,则此属性应返回 True,即他们已提供有效凭据。你需要确保 user_loader 回调返回的代表你的用户的对象返回该方法的 True

  • is_active

    如果这是一个活跃用户,则此属性应返回 True - 除了经过身份验证之外,他们还激活了帐户,未被暂停,或者你的应用程序拒绝帐户的任何条件。非活动帐户可能无法登录。如果你没有此类机制,请从此方法返回 True

  • is_anonymous

    如果这是匿名用户,则此属性应返回 True。这意味着 user_loader 回调返回的用户对象应返回 True

  • get_id()

    此方法必须返回唯一标识此用户的 unicode,并且可用于从 user_loader 回调加载用户。请注意,这必须是 unicode - 如果 ID 本身是 int 或其他类型,则需要将其转换为 unicode。如果 user_loader 回调从数据库返回对象,则此方法很可能返回此特定用户的数据库 ID。相同的 ID 当然应该导致 user_loader 回调稍后返回同一个用户。

如果你想使事情变得更容易为自己(**它实际上是推荐),你可以继承 UserMixinuser_loader 回调(大概数据库模型)返回的对象。你可以在此处查看默认情况下如何实现这些方法和属性。

记录用户

该扩展程序将验证用户输入的用户名和密码。事实上,如果你使用用户名和密码组合或其他机制,扩展程序并不在意。这是使用用户名和密码记录用户的示例。

@app.route('/login', methods=['GET', 'POST'])
def login():
    # Here we use a class of some kind to represent and validate our
    # client-side form data. For example, WTForms is a library that will
    # handle this for us, and we use a custom LoginForm to validate.
    form = LoginForm()
    if form.validate_on_submit():
        # Login and validate the user.
        # user should be an instance of your `User` class
        login_user(user)

        flask.flash('Logged in successfully.')

        next = flask.request.args.get('next')
        # is_safe_url should check if the url is safe for redirects.
        # See http://flask.pocoo.org/snippets/62/ for an example.
        if not is_safe_url(next):
            return flask.abort(400)

        return flask.redirect(next or flask.url_for('index'))
    return flask.render_template('login.html', form=form)

通常,日志记录用户是通过调用 login_user 并将表示前面提到的用户的对象实例传递给它来完成的。如图所示,这通常发生在从数据库中检索用户并验证其凭据之后,但是在此示例中,用户对象只是神奇地出现。

我已登录用户,现在怎么办?

user_loader 回调返回的对象可以通过多种方式访问​​。

  • 在模板中:

    扩展使用模板上下文处理器以名称 current_user 自动注入它。要禁用该行为,请在 LoginManager 构造函数中使用自定义处理器集 add_context_processor=False

      {% if current_user.is_authenticated %}
        Hi {{ current_user.name }}!
      {% endif %}
    
  • 在 Python 代码中:

    扩展提供了一个名为 current_user 的请求绑定对象。

      from flask_login import current_user    
    
      @app.route("/hello")
      def hello():
          # Assuming that there is a name property on your user object
          # returned by the callback
          if current_user.is_authenticated:
              return 'Hello %s!' % current_user.name 
          else:
              return 'You are not logged in!'
    
  • 使用装饰器快速限制访问 login_required 装饰器可用于快速限制访问。

      from flask_login import login_required
    
      @app.route("/settings")
      @login_required
      def settings():
          pass
    

记录用户

用户可以通过调用 logout_user() 注销 。即使用户没有登录也似乎是安全的,因此 @login_required 装饰器很可能被省略。

@app.route("/logout")
@login_required
def logout():
    logout_user()
    return redirect(somewhere)

如果用户未登录并访问 current_user 对象会发生什么?

通过 defult 返回 AnonymousUserMixin

  • is_activeis_authenticatedFalse
  • is_anonymousTrue
  • get_id() 返回 None

要为匿名用户使用不同的对象,请提供一个可调用的(类或工厂函数),为你的 LoginManager 创建匿名用户:

login_manager.anonymous_user = MyAnonymousUser

接下来是什么?

以上是对扩展的基本介绍。要了解有关配置和其他选项的更多信息,强烈建议你阅读官方指南