Flask-Login安装
pip install flask-login
初始化
除了实例化扩展类之外,我们还要实现一个“用户加载回调函数”,具体代码如下所示:
from flask_login import LoginManager
login_manager = LoginManager(app) # 实例化扩展类
@login_manager.user_loader
def load_user(user_id): # 创建用户加载回调函数,接受用户 ID 作为参数
user = User.query.get(int(user_id)) # 用 ID 作为 User 模型的主键查询对应的用户
return user # 返回用户对象
Flask-Login 提供了一个 current_user 变量,注册这个函数的目的是,当程序运行后,如果用户已登录, current_user 变量的值会是当前用户的用户模型类记录。
另一个步骤是让存储用户的 User 模型类继承 Flask-Login 提供的 UserMixin 类:
from flask_login import UserMixin
class User(db.Model, UserMixin):
# ...
继承这个类会让 User 类拥有几个用于判断认证状态的属性和方法,其中最常用的是 is_authenticated 属性:如果当前用户已经登录,那么 current_user.is_authenticated 会返回 True, 否则返回 False。有了 current_user 变量和这几个验证方法和属性,我们可以很轻松的判断当前用户的认证状态。
登录示例
登录用户使用 Flask-Login 提供的 login_user() 函数实现,需要传入用户模型类对象作为参数。下面是用于显示登录页面和处理登录表单提交请求的视图函数:
from flask_login import login_user
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if not username or not password:
flash('Invalid input.')
return redirect(url_for('login'))
user = User.query.first()
# 验证用户名和密码是否一致
if username == user.username and user.validate_password(password):
login_user(user) # 登入用户
flash('Login success.')
return redirect(url_for('index')) # 重定向到主页
flash('Invalid username or password.') # 如果验证失败,显示错误消息
return redirect(url_for('login')) # 重定向回登录页面
return render_template('login.html')
登出示例
和登录相对,登出操作则需要调用 logout_user() 函数,使用下面的视图函数实现:
from flask_login import login_required, logout_user
@app.route('/logout')
@login_required # 用于视图保护
def logout():
logout_user() # 登出用户
flash('Goodbye.')
return redirect(url_for('index')) # 重定向回首页
认证保护
在 Web 程序中,有些页面或 URL 不允许未登录的用户访问,而页面上有些内容则需要对未登陆的用户隐藏,这就是认证保护。包括:
- 视图保护
- 模板内容保护
视图保护
在视图保护层面来说,未登录用户不能执行下面的操作:
- 不能对数据进行增加、删除、修改操作。
对于不允许未登录用户访问的视图,只需要为视图函数附加一个 login_required 装饰器就可以将未登录用户拒之门外。以删除条目视图为例:
@app.route('/movie/delete/<int:movie_id>', methods=['POST'])
@login_required # 登录保护
def delete(movie_id):
movie = Movie.query.get_or_404(movie_id)
db.session.delete(movie)
db.session.commit()
flash('Item deleted.')
return redirect(url_for('index'))
添加了这个装饰器后,如果未登录的用户访问对应的 URL,Flask-Login 会把用户重定向到登录页面,并显示一个错误提示。为了让这个重定向操作正确执行,我们还需要把 login_manager.login_view 的值设为我们程序的登录视图端点(函数名),把下面这一行代码放到 login_manager 实例定义下面即可:
login_manager.login_view = 'login'
需要的话,可以通过设置 login_manager.login_message 来自定义错误提示消息。
创建新条目的操作稍微有些不同,因为对应的视图同时处理显示页面的 GET 请求和创建新条目的 POST 请求,我们仅需要禁止未登录用户创建新条目,因此不能使用 login_required,而是在函数内部的 POST 请求处理代码前进行过滤:
from flask_login import login_required, current_user
# ...
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
if not current_user.is_authenticated: # 如果当前用户未认证
return redirect(url_for('index')) # 重定向到主页
# ...
模板内容保护
认证保护的另一形式是页面模板内容的保护。比如,不能对未登录用户显示下列内容:
- 增加、删除、修改的元素
这几个元素的定义都在首页模板(index.html)中,以创建新条目表单为例,我们在表单外部添加一个 if 判断:
<!-- 在模板中可以直接使用 current_user 变量 -->
{
% if current_user.is_authenticated %}
<form method="post">
Name <input type="text" name="title" autocomplete="off" required>
Year <input type="text" name="year" autocomplete="off" required>
<input class="btn" type="submit" name="submit" value="Add">
</form>
{
% endif %}