pip install flask-login

接下来创建扩展对象实例:

from flask import Flask
from flask_login import LoginManager app = Flask(__name__)
login_manager = LoginManager(app)

同时,你可以对LoginManager对象赋上配置参数:

# 设置登录视图的名称,如果一个未登录用户请求一个只有登录用户才能访问的视图,
# 则闪现一条错误消息,并重定向到这里设置的登录视图。
# 如果未设置登录视图,则直接返回401错误。
login_manager.login_view = 'login'
# 设置当未登录用户请求一个只有登录用户才能访问的视图时,闪现的错误消息的内容,
# 默认的错误消息是:Please log in to access this page.。
login_manager.login_message = 'Unauthorized User'
# 设置闪现的错误消息的类别
login_manager.login_message_category = "info"

编写用户类

使用Flask-Login之前,你需要先定义用户类,该类必须实现以下三个属性和一个方法:

属性 is_authenticated

  当用户登录成功后,该属性为True。

属性 is_active

  如果该用户账号已被激活,且该用户已登录成功,则此属性为True。

属性 is_anonymous

  是否为匿名用户(未登录用户)。

方法 get_id()

  每个用户都必须有一个唯一的标识符作为ID,该方法可以返回当前用户的ID,这里ID必须是Unicode。

因为每次写个用户类很麻烦,Flask-Login提供了”UserMixin”类,你可以直接继承它即可:

from flask_login import UserMixin

class User(UserMixin):
pass

从会话或请求中加载用户

在编写登录登出视图前,我们要先写一个加载用户对象的方法。它的功能是根据传入的用户ID,构造一个新的用户类的对象。为了简化范例,我们不引入数据库,而是在列表里定义用户记录。

# 用户记录表
users = [
{'username': 'Tom', 'password': ''},
{'username': 'Michael', 'password': ''}
] # 通过用户名,获取用户记录,如果不存在,则返回None
def query_user(username):
for user in users:
if user['username'] == username:
return user # 如果用户名存在则构建一个新的用户类对象,并使用用户名作为ID
# 如果不存在,必须返回None
@login_manager.user_loader
def load_user(username):
if query_user(username) is not None:
curr_user = User()
curr_user.id = username
return curr_user

上述代码中,通过”@login_manager.user_loader”装饰器修饰的方法,既是我们要实现的加载用户对象方法。它是一个回调函数,在每次请求过来后,Flask-Login都会从Session中寻找”user_id”的值,如果找到的话,就会用这个”user_id”值来调用此回调函数,并构建一个用户类对象。因此,没有这个回调的话,Flask-Login将无法工作。

有一个问题,启用Session的话一定需要客户端允许Cookie,因为Session ID是保存在Cookie中的,如果Cookie被禁用了怎么办?那我们的应用只好通过请求参数将用户信息带过来,一般情况下会使用一个动态的Token来表示登录用户的信息。此时,我们就不能依靠”@login_manager.user_loader”回调,而是使用”@login_manager.request_loader”回调。

from flask import request

# 从请求参数中获取Token,如果Token所对应的用户存在则构建一个新的用户类对象
# 并使用用户名作为ID,如果不存在,必须返回None
@login_manager.request_loader
def load_user_from_request(request):
username = request.args.get('token')
if query_user(username) is not None:
curr_user = User()
curr_user.id = username
return curr_user

为了简化代码,上面的例子就直接使用用户名作为Token了,实际项目中,大家还是要用一个复杂的算法来验证Token。

登录及登出

一切准备就绪,我们开始实现登录视图:

from flask import render_template, redirect, url_for, flash
from flask_login import login_user @app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form.get('username')
user = query_user(username)
# 验证表单中提交的用户名和密码
if user is not None and request.form['password'] == user['password']:
curr_user = User()
curr_user.id = username # 通过Flask-Login的login_user方法登录用户
login_user(curr_user) # 如果请求中有next参数,则重定向到其指定的地址,
# 没有next参数,则重定向到"index"视图
next = request.args.get('next')
return redirect(next or url_for('index')) flash('Wrong username or password!')
# GET 请求
return render_template('login.html')

上述代码同之前Login视图最大的不同就是你在用户验证通过后,需要调用Flask-Login扩展提供的”login_user()”方法来让用户登录,该方法需传入用户类对象。这个”login_user()”方法会帮助你操作用户Session,并且会在请求上下文中记录用户信息。另外,在具体实现时,建议大家对”next”参数值作验证,避免被URL注入攻击。

“login.html”模板很简单,就是显示一个用户名密码的表单:

<!doctype html>
<title>Login Sample</title>
<h1>Login</h1>
{% with messages = get_flashed_messages() %}
<div>{{ messages[0] }}</div>
{% endwith %}
<form action="{{ url_for('login') }}" method="POST">
<input type="text" name="username" id="username" placeholder="Username"></input>
<input type="password" name="password" id="password" placeholder="Password"></input>
<input type="submit" name="submit"></input>
</form>

接下来,让我们写个index视图

from flask_login import current_user, login_required

@app.route('/')
@login_required
def index():
return 'Logged in as: %s' % current_user.get_id()

装饰器”@login_required”确保只有登录用户才能访问这个index视图,Flask-Login帮我们实现了这个装饰器。如果用户未登录,它就会将页面重定向到登录视图,也就是我们在第一节中配置的”login_manager.login_view”的视图。

同时,重定向的地址会自动加上”next”参数,参数的值是当前用户请求的地址,这样,登录成功后就会跳转回当前视图。可以看到我们对于用户登录所需要的操作,这个装饰器基本都实现了

Flask-Login还提供了”current_user”代理,可以访问到登录用户的用户类对象。我们在模板中也可以使用这个代理。让我们再写一个home视图:

@app.route('/home')
@login_required
def home():
return render_template('hello.html')

模板代码如下:

<!doctype html>
<title>Login Sample</title>
{% if current_user.is_authenticated %}
<h1>Hello {{ current_user.get_id() }}!</h1>
{% endif %}

在上面的模板代码中,我们直接访问了”current_user”对象的属性和方法。

登出视图也很简单,Flask-Login提供了”logout_user()”方法来帮助你清理用户Session。

from flask_login import logout_user

@app.route('/logout')
@login_required
def logout():
logout_user()
return 'Logged out successfully!'

自定义未授权访问的处理方法

“@login_required”装饰器对于未登录用户访问的默认处理是重定向到登录视图,如果我们不想它这么做的话,可以自定义处理方法:

@login_manager.unauthorized_handler
def unauthorized_handler():
return 'Unauthorized'

这个”@login_manager.unauthorized_handler”装饰器所修饰的方法就会代替”@login_required”装饰器的默认处理方法。有了上面的代码,当未登录用户访问index视图时,页面就会直接返回”Unauthorized”信息。

Remember Me

在登录视图中,调用”login_user()”方法时,传入”remember=True”参数,即可实现“记住我”功能:

...
login_user(curr_user, remember=True)
...

Flask-Login是通过在Cookie实现的,它会在Cookie中添加一个”remember_token”字段来记住之前登录的用户信息,所以禁用Cookie的话,该功能将无法工作。

Fresh登录

当用户通过账号和密码登录后,Flask-Login会将其标识为Fresh登录,即在Session中设置”_fresh”字段为True。而用户通过Remember Me自动登录的话,则不标识为Fresh登录。对于”@login_required”装饰器修饰的视图,是否Fresh登录都可以访问,但是有些情况下,我们会强制要求用户登录一次,比如修改登录密码,这时候,我们可以用”@fresh_login_required”装饰器来修饰该视图。这样,通过Remember Me自动登录的用户,将无法访问该视图:

from flask_login import fresh_login_required

@app.route('/home')
@fresh_login_required
def home():
return 'Logged in as: %s' % current_user.get_id()

会话保护

Flask-Login自动启用会话保护功能。对于每个请求,它会验证用户标识,这个标识是由客户端IP地址和User Agent的值经SHA512编码而来。在用户登录成功时,Flask-Login就会将这个值保存起来以便后续检查。默认的会话保护模式是”basic”,为了加强安全性,你可以启用强会话保护模式,方法是配置LoginManager实例对象中的”session_protection”属性:

login_manager.session_protection = "strong"

在”strong”模式下,一旦用户标识检查失败,便会清空所用Session内容,并且Remember Me也失效。而”basic”模式下,只是将登录标为非Fresh登录。你还可以将”login_manager.session_protection”置为None来取消会话保护。

Flask 扩展 用户会话的更多相关文章

  1. Flask从入门到精通之重定向和用户会话

    最新版的hello.py 存在一个可用性问题.用户输入名字后提交表单,然后点击浏览器的刷新按钮,会看到一个莫名其妙的警告,要求在再次提交表单之前进行确认.之所以出现这种情况,是因为刷新页面时浏览器会重 ...

  2. flask 扩展之 -- flask-login

    一. 使用 Werkzeug 实现密码散列. generate_password_hash(password, method=pbkdf2:sha1, salt_length=8) 将原始密码作为输入 ...

  3. Flask 扩展 国际化 本地化

    pip install flask-babel 先初始化一个Flask-Babel的实例 from flask import Flask from flask.ext.babel import Bab ...

  4. 2.6、Flask扩展

    Flask 被设计为可扩展形式,故而没有提供一些重要的功能,例如数据库和用户认证,所以开发者可以自由选择最适合程序的包,或者按需求自行开发. 社区成员开发了大量不同用途的扩展,如果这还不能满足需求,你 ...

  5. Flask 扩展 自定义扩展

    创建一个为视图访问加日志的扩展Flask-Logging,并从中了解到写Flask扩展的规范. 创建工程 先创建一个工程,目录结构如下: flask-logging/ ├ LICENSE # 授权说明 ...

  6. Flask从入门到精通之flask扩展

    Flask被设计成可扩展形式,因此并没有提供一些重要的功能,比如数据库和用户认证,所以开发者可以自由选择最适合程序的包,或者按需求自行开发.社区成员开发了大量不同用途的扩展,如果这还不能满足需求,你还 ...

  7. Flask扩展实现HTTP令牌token认证HTTPTokenAuth

    Token认证 在restful设计中,用户认证模式通常使用json web token,而不会使用传统的HTTP Basic认证(传入账号密码) token认证模式如下:在请求header中加入to ...

  8. Flask扩展 -- flask-mail

    电子邮件是最常用的通信方式之一.虽然Python标准库中的smtplib包可用在Flask程序中发送电子邮件,但包装了smtplib的Flask-Mail扩展能更好的和Flask集成. 1.安装Fla ...

  9. Inside Flask - flask 扩展加载过程

    Inside Flask - flask 扩展加载过程 flask 扩展(插件)通常是以 flask_<扩展名字> 为扩展的 python 包名,而使用时,可用 import flask. ...

随机推荐

  1. [BZOJ1014] [JSOI2008] 火星人prefix (splay & 二分答案)

    Description 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀.比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号:序号: 1 2 3 4 5 6 7 ...

  2. angularjs中的下拉框默认选中

    1.  ng-init  属性: <!DOCTYPE html> <html> <head> <meta charset="utf-8"& ...

  3. C# Redis实战(一)

    一.初步准备 Redis 是一个开源的使用ANSI C 语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value 数据库.Redis的出现,很大程度补偿了memcached这类key/va ...

  4. 怎么修改无法启动的docker容器的配置?

    原因: 由于错误的配置导致原来可以启动的docker容器不能启动了.相信很多人的做法是删除容器重建一个,这样也是可以的,但是你的配置和插件就得重新安装,非常麻烦.最小的代价当然是修改原来的配置让他能启 ...

  5. 去除IE10自带的清除按钮

    最近在工作中碰到了一个问题,原本在IE8,IE9下正常的input表单,在IE10下会出现清除按钮,即表单右侧会出现一个可以清除该表单内容的小叉.由于之前一直没有兼容过IE10,所以我专门搜了下原因. ...

  6. java--计时器

    计时器 一.窗口化 public class Pro extends JFrame{ private JTextField textField = new JTextField(45);//系统时间文 ...

  7. SQL server 2008 安装提示:属性不匹配

    问题 安装SQL server 2008提示属性不匹配 解决方案 确保C盘以及其子文件夹C:\Program Files\Microsoft SQL Server和C:\Program Files ( ...

  8. JavaIO

    1.字节流和字符流 在IO有两种数据传输格式一个是字符流还一个是字节流,但是字符流就会涉及到编码的问题. 一开始美国使用的自己的编码表就是ASCII表 中国的字符需要被识别也需要编码表于是就有了GB2 ...

  9. window平台写的shell脚步在Linux不识别

    ---恢复内容开始--- 出现的问题是 写的shell脚步在Linux执行的时候不被识别 解决方案: 1.确保用户对文件有读写及执行权限 oracle@linux-106:~/RMAN/bin> ...

  10. UITableViewStyleGrouped模式下烦人的多余间距

    第一个section上边多余间距处理 // 隐藏UITableViewStyleGrouped上边多余的间隔 _tableView.tableHeaderView = [[UIView alloc] ...