Flask - Login
文档: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 ( 而不是抛出异常 )。
用户类
我们的用户模型除了用户名和密码等字段以外,还需要实现以下字段和方法:
-
is_authenticated
当用户通过验证时,也即提供有效证明时返回 True 。(只有通过验证的用户会满足login_required
的条件。) -
is_active
如果这是一个活动用户且通过验证,账户也已激活,未被停用,也不符合任何你 的应用拒绝一个账号的条件,返回 True 。不活动的账号可能不会登入(当然, 是在没被强制的情况下)。 -
is_anonymous
如果是一个匿名用户,返回 True 。(真实用户应返回 False 。) -
get_id()
返回一个能唯一识别用户的,并能用于从user_loader
回调中加载用户的 unicode 。注意着 必须 是一个 unicode —— 如果 ID 原本是 一个 int 或其它类型,你需要把它转换为 unicode 。
当然我们可以不去自己编写这些字段,因为 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'))