Flask - Login

2018-09-24  本文已影响111人  SingleDiego

文档:https://flask-login.readthedocs.io/en/latest/
中文文档1:http://docs.jinkan.org/docs/flask-login/
中文文档2:http://www.pythondoc.com/flask-login/
本文源码:https://github.com/SingleDiego/flask-login




安装

$ pip install flask-login




在应用中配置

初始配置方法:

from flask_login import LoginManager

login_manager = LoginManager()
login_manager.init_app(app)

然后我们需要定义一个 user_loader 回调,用来通过 唯一的 id 获取特定用户对象。例如这样:

# 这段代码通常放在定义 User 的 models 文件中
from app import login_manager

@login_manager.user_loader
def load_user(userid):
    # 这个方法由你使用的数据库来决定
    return User.get(userid)

如果 ID 无效,它应该返回 None ( 而不是抛出异常 )。




用户类

我们的用户模型除了用户名和密码等字段以外,还需要实现以下字段和方法:

当然我们可以不去自己编写这些字段,因为 Flask-Login 在 UserMixin 类中提供了他们, 我们只需要把用户模型继承它,它提供了对所有这些方法的默认实现。

from flask_login import UserMixin

class User(UserMixin, db.Model):
    ……

出于安全的考虑我们不应该储存用户的明文密码,我们使用 Werkzeug 包的方法来给密码进行哈希加密。

from flask_login import UserMixin
from werkzeug.security import (
    generate_password_hash, 
    check_password_hash
    )

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    password_hash = db.Column(db.String(128))

    def __repr__(self):
        return '<User {}>'.format(self.username)

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)




用户登录

用户通过验证后,用 login_user 函数来登入他们。下面是一个验证登陆的例子:

from flask_login import login_user,

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        username = form.data.get('username', None)
        password = form.data.get('password', None)

        user = User.query.filter(username==username).first()
        # 验证用户名和秘密,如正确执行登陆操作
        if user and user.check_password(password):
            flash(u'欢迎你:{}!'.format(user.username))
            login_user(user)
        else:
            flash(u'用户名或密码错误!')

        return redirect(url_for('index'))

    return render_template('login.html', title='Sign In', form=form)

使用 current_user 可以获取到登录的用户,如果用户未登录则会返回一个 flask_login.mixins.AnonymousUserMixin 实例。

在视图函数中直接引入后使用:

from flask_login import current_user

在模板中可直接使用:

{% if current_user.is_authenticated %}
    <a>当前用户:{{ current_user.username }}</a>
{% else %}
    <a>未登录</a>
{% endif %}




登录检查

需要用户登入才能访问的视图可以用 @login_required 装饰器来装饰。

from flask_login import login_required

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

未登录的情况下访问该视图 Flask-Login 会重定向到登录视图并闪现(Flash)一条消息。如果未设置登录视图,它将会以 401 错误退出。

登录视图在 login_manager.login_view 设置,例如:

login_manager.login_view = "login"

其参数等同于 url_for 中的参数。

默认的闪现消息是 “Please log in to access this page.”。要自定义该信息,请设置 LoginManager.login_message

login_manager.login_message = u"请先登录以浏览该页面。"

要自定义消息分类的话(消息分类详情见:消息闪现 一章),请设置 LoginManager.login_message_category

login_manager.login_message_category = "info"

当重定向到登入视图,它的请求字符串中会有一个 next 变量,其值为用户之前访问的页面。我们可以这样获取他:

next = request.args.get('next')

利用这个变量我们可以实现登陆后跳转到登陆前所在页面的功能:

from werkzeug.urls import url_parse

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        username = form.data.get('username', None)
        password = form.data.get('password', None)
        remember_me = form.data.get('remember_me')

        user = User.query.filter_by(username=username).first()

        if user and user.check_password(password):
            flash(u'欢迎你:{}!'.format(user.username))
            login_user(user)
            # 登录后跳转到登陆前所在页面
            next = request.args.get('next')
            # 如果 next 为空或包含域名,判断为不合法的参数
            if next == None or url_parse(next).netloc != '':
                return redirect(url_for('index'))
            else:
                return redirect(next)
        else:
            flash(u'用户名或密码错误!')

        return redirect(url_for('index'))
    return render_template('login.html', title='Sign In', form=form)

对于 next 参数,我们必须进行校验以防跳转到其他恶意网址。正常情况下,next 参数应该是一个相对路径(如:/test),攻击者可能会在 next 参数中附上包含域名的完整 URL 来达到跳转到恶意链接的目的。我们应当校验这种情况。




用户登出

通过 Flask-Login 的 logout_user() 函数来实现登出功能

from flask_login import logout_user

@app.route('/logout')
def logout():
    logout_user()
    return redirect(url_for('index'))
上一篇下一篇

猜你喜欢

热点阅读