安装和启用

遵循标准的Flask扩展安装和启用方式,先通过pip来安装扩展:

$ pip install Flask-Login

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

1

2

3

4

5

from flask import Flask

from flask.ext.login import LoginManager

app = Flask(__name__)

login_manager = LoginManager(app)

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

1

2

3

4

5

6

7

8

9

# 设置登录视图的名称,如果一个未登录用户请求一个只有登录用户才能访问的视图,

# 则闪现一条错误消息,并重定向到这里设置的登录视图。

# 如果未设置登录视图,则直接返回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之前,你需要先定义用户类,该类必须实现以下三个属性和一个方法:

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

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

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

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

  1. 属性 is_authenticated
  2. 属性 is_active
  3. 属性 is_anonymous
  4. 方法 get_id()

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

1

2

3

4

from flask.ext.login import UserMixin

class User(UserMixin):

pass

从会话或请求中加载用户

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

# 用户记录表

users = [

{'username': 'Tom', 'password': '111111'},

{'username': 'Michael', 'password': '123456'}

]

# 通过用户名,获取用户记录,如果不存在,则返回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”回调。

1

2

3

4

5

6

7

8

9

10

11

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。

登录及登出

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

from flask import render_template, redirect, url_for, flash

from flask.ext.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”模板很简单,就是显示一个用户名密码的表单:

1

2

3

4

5

6

7

8

9

10

11

<!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视图:

1

2

3

4

5

6

from flask.ext.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视图:

1

2

3

4

@app.route('/home')

@login_required

def home():

return render_template('hello.html')

模板代码如下:

1

2

3

4

5

<!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。

1

2

3

4

5

6

7

from flask.ext.login import logout_user

@app.route('/logout')

@login_required

def logout():

logout_user()

return 'Logged out successfully!'

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

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

1

2

3

@login_manager.unauthorized_handler

def unauthorized_handler():

return 'Unauthorized'

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

Remember Me

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

1

2

3

...

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自动登录的用户,将无法访问该视图:

1

2

3

4

5

6

from flask.ext.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”属性:

1

login_manager.session_protection = "strong"

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

更多参考资料

Flask-Login的官方文档
Flask-Login的源码

本篇的示例代码可以在这里下载。

格式日志发布于2016年4月29日作者Billy.J.Hee分类Python、编程标签Flask、Login、Session

文章导航

上一上篇文章:Flask扩展系列(七)–表单

下一下篇文章:Flask扩展系列(九)–HTTP认证

《Flask扩展系列(八)–用户会话管理》有3个想法

  1. 张莹说道:

    2017年5月7日 下午9:42

    利用Flask-Login和session进行用户登录管理具体区别是什么呢?

    如何利用session记录其他需要保留的信息,例如上次访问页面的时间?

    多谢赐教!

Flask扩展系列(八)–用户会话管理相关推荐

  1. SAP Commerce Cloud UI 的用户会话管理

    这是 Jerry 2021 年的第 51 篇文章,也是汪子熙公众号总共第 328 篇原创文章. 如无特殊说明,本公众号介绍的 SAP Commerce Cloud UI,均指新一代基于 Spartac ...

  2. SAP Commerce Cloud UI(Spartacus Storefront) 的用户会话管理

    这是 Jerry 2021 年的第 51 篇文章,也是汪子熙公众号总共第 328 篇原创文章. 如无特殊说明,本公众号介绍的 SAP Commerce Cloud UI,均指新一代基于 Spartac ...

  3. 浅谈电商网站开发中用户会话管理机制的设计和实现原理

    笔者由于工作需要,最近对国内外两款知名的电商网站的用户会话管理(User Session Management) 的实现机制做了一些调研,这里把我学习到的一些知识分享给各位同行,希望起到抛砖引玉的作用 ...

  4. WebRTC 系列之音频会话管理

    导读:WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音对话或视频对话的 API.W3C 和 IETF 在2021年1月26日共同宣布 WebRTC ...

  5. Git学习系列(八)标签的管理及配置别名详解

    标签管理 通常咱们发布(release)一个版本时需要打标签,这样可以方便咱们以后要取某个标签版本时,直接把那个相应标本的历史版本取出来即可,它其实是一个版本快照. 标签和分支有点像,都是指向某个co ...

  6. datastore_使用Spring Session和JDBC DataStore进行会话管理

    datastore 在Web应用程序中,用户会话管理对于管理用户状态至关重要. 在本文中,我们将学习在集群环境中管理用户会话所采用的方法,以及如何使用Spring Session以更简单和可扩展的方式 ...

  7. 使用Spring Session和JDBC DataStore进行会话管理

    在Web应用程序中,用户会话管理对于管理用户状态至关重要. 在本文中,我们将学习在集群环境中管理用户会话所遵循的方法,以及如何使用Spring Session以更加简单和可扩展的方式实现它. 通常在生 ...

  8. SpringBoot实现用户统一管理与单点登陆

    前言 最近在开发产品的过程中,需要将业务功能拆分成独立子系统,既可以单独使用也可以集成部署,这里就需要对框架进行扩展,支持用户统一管理与单点登陆.我们的基础框架使用redis实现token认证,所以只 ...

  9. js如何获取jwt信息_学习后端鉴权系列: 基于JWT的会话管理

    内容回顾 上一节讲了基于Cookie+Session的认证方案. Even:学习后端鉴权系列: 基于Cookie, Session认证​zhuanlan.zhihu.com 由于基于Session方案 ...

最新文章

  1. 10种避免大型部署的方法
  2. 在线IDE之关键字另色显示
  3. python网站开发实例视频_Python实战-让在职教育类网站的视频全自动播放
  4. 你知道吗?其实 Oracle 直方图自动统计算法存在这些缺陷!(附验证步骤)
  5. 安装Win7系统时使用diskpart命令将GPT分区转换为MBR分区
  6. float js 正则 验证_爬虫篇 | 200 行代码实现一个滑动验证码
  7. 深入理解Scala的隐式转换
  8. 字节跳动将推出汽车云业务,计划2025年追赶腾讯
  9. Python模块之optparse
  10. 在Biztalk应用中调用程序集的方法
  11. HighCharts:柱状图设置不同柱子不同颜色
  12. 流程控制之if...else
  13. win10/win7 usb转串口驱动下载
  14. JAVA拓展新的数据库,SqlFaker:轻量级、易拓展的Java数据库智能填充开源库
  15. 蓝桥杯每日一练----字符串逆序
  16. next. js_Next.js添加到您的应用程序中的图标
  17. [mysql] 变量、处理程序和流程控制
  18. NAS硬盘存储服务器维修,NAS存储服务器用NAS硬盘的原因有哪些?NAS存储硬盘该如何选择?...
  19. 东北人都难懂的东北话
  20. Python3: fp-growth频繁项集求解算法代码(提供py文件,可直接调用)

热门文章

  1. merge into ORA-30926
  2. CDZSC_2015寒假新人(1)——基础 i
  3. Oracle内部错误:ORA-07445[kcflfi()+466] [INT_DIVIDE_BY_ZERO]一例
  4. 好久不更新这个博客了。
  5. 深入理解InnoDB(5)-文件系统
  6. leetcode 49. 字母异位词分组(排序+hash)
  7. lambda 使用_如何使用Lambda和API网关构建API
  8. 未来编程语言的走向_在编程方面我从失败走向成功的过程以及让我成功的原因
  9. 通过在Chipotle用餐了解模板方法设计模式
  10. pandas之DataFrame合并merge