使用flask构建一个WEB管理系统,肯定要有登入登出需求的。这里使用flask的一个扩展flask-login来实现这个功能。
Flask-Login
是 Flask 中一个用于处理用户登录状态的扩展,它简化了用户认证的处理。它可以轻松地集成到 Flask 网站中,并提供了处理用户会话、保护路由等功能。
写在前面的话
在本示例中,并没有使用前文数据模型中的Account模型作为用户账号,同样的也没有将此模型作为flask-login的账号,因为在设计之初就将账号功能接入了已有的统一登录中。具体会在下一篇文档中详解。
Flask-Login的使用
Flask-Login 的主要功能:
- 用户会话管理: 管理用户的登录状态,使得用户在登录后可以访问受保护的视图,而未登录用户将被重定向到登录页面。
- 保护路由: 通过
@login_required
装饰器,可以轻松保护需要用户登录才能访问的路由。 - 当前用户对象: 提供一个
current_user
对象,可以方便地访问当前登录的用户对象。 - 记住我功能: 支持"记住我"功能,使用户在关闭浏览器后仍然保持登录状态。
初始化LoginManager
首先要初始化Flask-Login。在Flask应用中初始化LoginManager
app = Flask(__name__, template_folder="templates")
login_manager = LoginManager(app) # Setup a Flask-Login Manager
创建用户模型
创建一个用户模型,并确保它包含一个唯一的标识符,例如用户的 ID,这里
class User:
def __init__(self, user_id):
self.id = user_id
加载用户回调函数
这里使用redis存储用户信息。
# 用户加载函数
@login_manager.user_loader
def load_user(user_id):
return User(user_id)
Flask-Login配置项
Flask-Login 提供了一系列的配置项,你可以通过配置 LoginManager
对象的属性来定制其行为。以下是一些常见的 Flask-Login 配置项:
login_view
: 设置登录页面的端点名称。当用户需要登录时,Flask-Login 会重定向到这个页面。默认值为None
,表示未配置登录页面,此时会返回 401 Unauthorized 错误。
login_manager.login_view = 'login'
login_message
和login_message_category
: 分别设置登录提示消息和消息的类别。当用户需要登录时,可以通过这两个配置项设置登录提示消息。
login_manager.login_message = '请先登录'
login_manager.login_message_category = 'info'
login_message_fallback
: 设置登录提示消息的备用消息,当当前会话中不存在消息时使用。默认为None
。
login_manager.login_message_fallback = '登录过期,请重新登录'
refresh_view
和refresh_message
: 设置刷新页面的端点名称和刷新提示消息。用于处理会话刷新的逻辑。
login_manager.refresh_view = 'refresh'
login_manager.refresh_message = '会话已刷新'
needs_refresh_message
: 设置需要刷新的消息。当用户在进行敏感操作时,如果会话即将过期,可以通过此配置项设置提示消息。
login_manager.needs_refresh_message = '请重新登录以继续'
expired_message
和expired_message_category
: 设置会话过期的提示消息和消息的类别。
login_manager.expired_message = '会话已过期,请重新登录'
login_manager.expired_message_category = 'warning'
session_protection
: 设置会话保护的级别,可以是None
、basic
、strong
中的一个。默认为None
。
None
:不启用会话保护。basic
:启用基本的会话保护,检测 IP 地址和用户代理是否变化。strong
:启用强制的会话保护,检测 IP 地址、用户代理和用户 ID 是否变化。
login_manager.session_protection = 'strong'
这些配置项可以根据你的应用需求进行定制,确保在初始化 LoginManager
对象后进行配置。例如:
from flask import Flask
from flask_login import LoginManager
app = Flask(__name__)
login_manager = LoginManager(app)
# 设置登录页面的端点名称
login_manager.login_view = 'login'
# 设置登录提示消息
login_manager.login_message = '请先登录'
# 设置刷新页面的端点名称
login_manager.refresh_view = 'refresh'
# 设置会话保护级别为 'strong'
login_manager.session_protection = 'strong'
login_required装饰器
@login_required
装饰器是 Flask-Login 扩展提供的一个装饰器,用于保护需要用户登录才能访问的视图函数。使用这个装饰器,可以地限制某些路由只能被已登录用户访问。
注意,在使用login_required装饰器的时候,要将其卸载路由装饰器后面.
登入登出路由
@app.route('/login', methods=["GET", "POST"])
def login():
"""
:return:
"""
username = request.args.get('username', None)
if username:
user = User(username)
login_user(user)
session['user_id'] = username
return '当前用户为:%s' % username
else:
return '请先登录'
@app.route('/logout')
@login_required
def logout():
"""
退出登录
:return:
"""
# --start-- 本地系统logout
logout_user()
session.pop('user_id', None)
return redirect(url_for('index'))
完整代码
from flask import Flask, redirect, request, session, url_for
from db import db
from flask_login import LoginManager, login_user, UserMixin, current_user, login_required, logout_user
app = Flask(__name__, template_folder="templates")
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db' # 使用SQLite数据库,可以根据需要更改
app.config['SECRET_KEY'] = "x0jasj082-gam=asesfda89j"
# secret_key flask和扩展使用它来保证数据安全, 它被设置为'dev'开发期间提供一个方便的值,但在部署时应随机覆盖它
db.init_app(app) # 初始化绑定DB 与 Flask
login_manager = LoginManager(app) # Setup a Flask-Login Manager
login_manager.login_view = "login" # required_login 校验不通过,默认跳转
class User(UserMixin):
"""
flask-login 必要 User类
"""
def __init__(self, username):
self.username = username
def __repr__(self):
return self.username
def get_id(self):
return self.username
# 用户加载函数
@login_manager.user_loader
def load_user(user_id):
return User(user_id)
# 定义错误处理函数,捕获404错误
@app.errorhandler(404)
def page_not_found(error):
# 重定向到根路由
return redirect(url_for('index'))
# 定义根路由
@app.route('/')
@login_required
def index():
user = current_user
return '欢迎你, ' + user.username
@app.route('/login', methods=["GET", "POST"])
def login():
"""
:return:
"""
username = request.args.get('username', None)
if username:
user = User(username)
login_user(user)
session['user_id'] = username
return '当前用户为:%s' % username
else:
return '请先登录'
@app.route('/logout')
@login_required
def logout():
"""
退出登录
:return:
"""
# --start-- 本地系统logout
logout_user()
session.pop('user_id', None)
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)
运行效果
未登录情况先访问/,跳转到login
未登录情况下,不携带args访问/login
携带args访问/login模拟登陆
登陆后访问/