如下图所示,这是我们在之前的实验中编写的登录视图函数,它在 handlers/front.py 文件中:

当我们点击登录提交按钮后,首先进行第 42 行的表单验证,通过后,根据邮箱查询 user 数据表中对应的用户信息,然后调用 Flask-Login 提供的 login_user 方法登录。

接下来我们继续分析这个方法,它在 flask_login.utils 模块中:

在上一节实验我们对这个方法的参数做了说明:

  • user 就是 User 映射类的实例;
  • remember 是布尔值,当用户在表单中选中「记住我」时,remember 参数的值为 True
  • duration 是有效时间,也就是绿码的有效时间,在我们的项目中,它就是 session ,中文名字是「会话」;
  • force 表示强制登录,当用户处于封禁状态时,user.is_active 属性值为 False 。这种情况下 force 参数的值为 True 的话,会强行执行登录逻辑;
  • fresh 字面意思就是「新鲜」,对于敏感操作,比如修改密码,就需要用户处于新鲜的登录状态。例如用户登录时验证了信息,session 使得用户处于登录状态,但不是新鲜的登录状态。如果用户想修改密码,就需要重新验证一次信息,这次验证会设置 session ,使得本次验证后的用户处于新鲜的登录状态。这样起到对敏感操作的保护作用。

如上图所示,第 166 行处理 force 参数,判断是否强行登录。

第 169 行代码,current_app 就是应用对象,它的 login_manager 属性在前面的步骤中已经分析过,就是登录管理对象,LoginManager 类的实例。这个实例的 id_attribute 属性值是变量 ID_ATTRIBUTE ,这个变量来自配置文件 flask_login.config 模块,变量值是 'get_id' 。这一行等号前面的 user_id 变量值就是请求登录的用户在数据表中的 id 字段的值的字符串。

上节实验就介绍到这里,本节实验我们要继续了解后面的全部代码。

第 170 行 session['_user_id'] 的值也就是 id 值的字符串了。第 171 行 session['_fresh'] 的值默认是 True。

第 172 行需要着重说明,current_app.login_manager 就是登录管理类的实例,也就是 self ,所以后面的 _session_identifier_generator 就是在 flask_login.login_manager 模块中定义的属性,而这个属性的值就是当前模块下的 _create_identifier 函数:

如上图所示,第 367 行这里用到了请求头中的 User-Agent 字段,也就是用户代理字符串。

第 370 行 format 的第一个参数,_get_remote_addr 方法的返回值是请求的 IP 地址,所以等号前面的 base 就是用 | 隔开的一个字符串,它里面是请求的 IP 地址和用户代理。

最后利用 hashlib 模块提供的哈希功能将 base 转成十六进制哈希字符串并返回。这个字符串有什么用呢?

思考一下,我们在电脑上打开 A 浏览器登录某网站后,打开 B 浏览器再打开该网站,我们并未处于登录状态,只有 A 浏览器处于登录状态,为什么?因为服务器是依靠请求中的多项信息来识别用户,其中一项就是用户代理,A B 两个浏览器的用户代理字符串不同,就是真正的原因。

现在回到 login_user 方法的源码中,第 172 行 session['_id'] 的值就是上述哈希字符串

到这里为止,我们的 session 里已经有这些字段了:

  • csrf_token :这是在创建表单类实例时添加的;
  • _user_id :用户 ID 字符串;
  • _fresh :设置新鲜的登录状态;
  • _id :根据请求的 IP 和用户代理字符串哈希得到的字符串。

接下来,如果我们在登录前选中「记住我」,第 175 行就会再增加一个 _remember ,值是字符串 ‘set’ 。如果有 duration 参数的话,还会增加一个 _remember_seconds 字段。

继续向下,第 187 行 current_app 是 flask.globals 模块中定义的一个应用代理对象,可以把它看作应用对象,这个对象的 login_manager 属性就是 flask_login.login_manager 模块中定义的 LoginManager 类的实例,该实例的 _update_request_context_with_user 方法把 user 赋值给定义在 flask.ctx 模块中的请求上下文类 RequestContext 的实例的 user 属性,也就是说,请求上下文对象的 user 属性值就是当前要登录的用户对象。

这里需要提一下 app.py 文件中,创建登录管理对象时,从 flask_login.__init__ 模块引入对象时,会运行 flask_login.utils 模块:

这里的 current_user 变量与 current_app 、g 、request 、session 一样,都是全局代理对象。注意第 26 行的参数是匿名函数,_get_user 方法定义如下:


如上图所示,第 349 行返回的是请求上下文对象的 user 属性值,也就是当前要登录的那个用户对象。

再次回到 login_user 方法中,第 188 行是一种信号机制,暂时略过。最后第 189 行返回布尔值 True 。

登陆之后

视图函数处理完毕后,和第一次请求一样,要调用应用对象的 full_dispatch_request 方法,它定义在 flask.app 模块中:


如上图所示,最后一行调用应用对象的 finalize_request 方法,此方法会调用 process_response 方法:

如上图所示,这些方法在前面的步骤中已经介绍过,这次我们看下第 2256 和 2257 行,这里有个 funcs ,它是一个迭代器,里面至少有一个 _update_remember_cookie 方法,此方法属于 flask_login.login_manager 模块中的 LoginManager 类,我们看下源码:

如上图所示,这里会从 session 中拿出 _remember 字段,如果在页面选择「记住我」,上图 407 行就会调用登录管理对象的 _set_cookie 方法,后者会调用响应对象的 set_cookie 方法设置一个 Set-Cookie 到响应头中。

登录管理对象的 _set_cookie 方法会从 session 中拿到 _remember_seconds 字段的值,它就是 Cookie 的有效期。如果没有这个字段,就会使用默认的设置,这个默认值就是一年

最后把携带了 Set-Cookie 的响应对象返回给浏览器。

下次浏览器发起请求的时候,会带上 Cookies ,处理请求信息时,会调用令牌生成器将 session 字段对应的值进行解密获得一个字典对象,其中包括 _user_id(用户 ID)、csrf_token 、_id(浏览器识别)等重要键值对。利用这个字典对象生成请求上下文对象的 session 属性值,其实也就可以看做是 session 这个全局代理。最后利用 session 就可以判断该用户是否处于登录状态,Cookie 是否已过期。

这样就完成了登录功能的实现。

我们来整理一下 Flask 的登录流程:
  • 浏览器首次发起登录请求:请求上下文对象的 session 属性值可以看做是 session 这个代理对象,它里面存储了请求头中的 Cookie 信息,包括 _fresh 和 csrf_token 这两个字段;
  • 调用视图函数来处理请求,向 session 中添加用户 ID _user_id 和识别客户端身份的 _id 哈希值这两个字段,也可能会有 _remember 这个字段;
  • 视图函数处理完毕,如果 session 中有 _remember 字段,会单独取出并处理,向响应头中添加一个 Set-Cookie ;
  • 然后处理 session ,将其进行整体哈希,向响应头中添加一个 Set-Cookie

此后浏览器再发起请求,就会携带上述 Cookies ,**服务器收到请求后就把这些信息用令牌生成器解密获得一个字典对象,并利用这个字典对象创建 session 。**最后根据 session 中的信息判断是哪位用户发来的请求以及该用户是否处于登录状态。

flask 登录功能流程源码分析相关推荐

  1. 二次开发:flowable审批流程实践与创建流程源码分析

    二次开发:flowable审批流程实践与创建流程源码分析 上一篇已经描述了基于开源项目https://doc.iocoder.cn/的flowable的快速开发,创建了一个租户,创建了用户和相应的岗位 ...

  2. 【SRIO】5、Xilinx RapidIO核例子工程源码分析

    目录 一.软件平台与硬件平台 二.打开例子工程 三.例子工程详解 3.1 工程概述 3.2 工程结构 3.3 工程分析 四.工程源码分析 3.1 顶层模块srio_example_top.v源码分析 ...

  3. 5.Xilinx RapidIO核例子工程源码分析

    https://www.cnblogs.com/liujinggang/p/10091216.html 一.软件平台与硬件平台 软件平台: 操作系统:Windows 8.1 64-bit 开发套件:V ...

  4. Android系统默认Home应用程序(Launcher)的启动过程源码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还须要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...

  5. 小明分享|8ms平台下工程源码分析

    今天小明为大家分享的是开发工具平台-8ms(www.8ms.xyz)工程源码分析 1.打开"8ms平台",创建工程制作完UI后,选中"编译"一栏,等待结束后,选 ...

  6. SpringMVC执行流程源码分析

    SpringMVC执行流程源码分析 我们先来看张图片,帮助我们理解整个流程 然后我们开始来解析 首先SpringMVC基于Servlet来运行 那么我们首先来看HttpServletBean这个类 他 ...

  7. 【源码分析】storm拓扑运行全流程源码分析

    [源码分析]storm拓扑运行全流程源码分析 @(STORM)[storm] 源码分析storm拓扑运行全流程源码分析 一拓扑提交流程 一stormpy 1storm jar 2def jar 3ex ...

  8. java spring 登录验证_浅析Spring Security登录验证流程源码

    一.登录认证基于过滤器链 Spring Security的登录验证流程核心就是过滤器链.当一个请求到达时按照过滤器链的顺序依次进行处理,通过所有过滤器链的验证,就可以访问API接口了. SpringS ...

  9. Android应用程序启动Binder线程源码分析

    Android的应用程序包括Java应用及本地应用,Java应用运行在davik虚拟机中,由zygote进程来创建启动,而本地服务应用在Android系统启动时,通过配置init.rc文件来由Init ...

最新文章

  1. LeetCode简单题之有多少小于当前数字的数字
  2. ML基石_4_FeasibilityOfLearning
  3. SQL Server2008(二)各版本的功能和主要应用范围
  4. erlang精要(3)-变量、原子与布尔代数
  5. 如何使用idea远程debug调试代码详解
  6. 还在发愁linux命令记不住吗?神器来了!
  7. 【搜索引擎Jediael开发4】V0.01完整代码
  8. 台式计算机单核与双核,什么是单核cpu、双核cpu 单核cpu和双核cpu的区别是什么...
  9. 51nod1244 欧拉函数之和 杜教筛
  10. linux ntp 'ntp_request.c'远程拒绝服务漏洞,NTP 'ntp_request.c'远程拒绝服务漏洞
  11. 标准输入输出(C++)
  12. 欧式距离、曼哈顿距离、余弦相似度(python代码)
  13. python 字符串不区分大小写_还在吐槽文本字符串难以处理,Python的这个绝活你还不知道
  14. 25.4. Phing
  15. JAVA的Date类与Calendar类
  16. python3 文件处理
  17. CTU 2019 Open Contest G.Beer Mugs
  18. c++ 调用labview_Namisoft解析基于Labview的自动化精密阻抗分析系统
  19. 深入浅出数据分析 - 直方图
  20. Bluemix RSA Private key cannot be used to encrypt

热门文章

  1. oracle select into 查询没有记录的解决办法
  2. 基于 FFmpeg 的跨平台视频播放器简明教程(四):像素格式与格式转换
  3. asp.net mvc源码分析-Controllerl篇 ControllerDescriptor
  4. 【CSS】5分钟带你彻底搞懂 W3C IE 盒模型
  5. Linux内核启动流程(vmlinux)
  6. 为配合疫苗接种,亚洲各国政府应制定更为灵活的隔离方案
  7. 数据库乐观锁和悲观锁的理解
  8. termios结构体的详细设置
  9. android adb 点亮 关闭 屏幕 命令
  10. percona mysql 安装_MySQL-percona安装