flask-login原理详解
最近发现项目中使用的flask-login中有些bug,直接使用官网的方式确实可以用,但仅仅是可以用,对于原理或解决问题没有什么帮助,最近通过查看网上资料、分析源码、通过demo、从零开始总结了flask-login的使用方式及内部实现原理。
先说使用,安装组件就不说了,太简单。
安装好了之后就是把组件注册到flask中,这个简单说下,具体flask如何注册这些扩展的原理后续再补上,引用官网的说法:登录管理(login manager)包含了让你的应用和 Flask-Login 协同工作的代码,比如怎样从一个 ID 加载用户,当用户需要登录的时候跳转到哪里等等。具体注册代码如下:。
# encoding:utf-8
from flask.ext.login import LoginManager
app = Flask(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "login" #配置如果需要登录调整的路由
注册好了之后,就可以使用了,以下是路由函数的写法,具体login_required、login_user的实现原理后面会再说
@app.route('/')
@login_required #进入首页需要判断用户是否登入,没有登录则跳转到注册时配置的路由
def index():
return render_template('index.html')
@app.route('/login', methods = ['POST', 'GET'])
def login():
if request.method == 'POST':
req = request.get_json()
username = req['username']
userpassword = req['userpassward']
if auth_user(username, userpassword): #判断用户名密码是否正确,这里随便写一个方法,写死用户名密码就可以
login_user(User(14,'root', 'root')) #用户名密码验证通过调用login_user把user注册到请求上下文session中,这个session其实就是一个LocalStack
return url_for('index')
flash(u'无效的用户名或密码')
return render_template('login.html')
还差一部分,创建models模块,用处两个,一个用来定义User类,二用来注册回调函数,这个回调函数通过user_id返回User实例。这里只是想弄清楚login的原理所有没用数据库,尽量聚焦
from flask.ext.login import UserMixin
from app import login_manager class User(UserMixin):
__tablename__ = 'users' def __init__(self, id,password, username):
'''
:param id:
:param username:
'''
self.id = id #这个属性一定要有,否则自己要重写get_id方法,不信自己去试下
self.password = password
self.username = username def __repr__(self):
return '<User %r>' % self.username @login_manager.user_loader
def load_user(user_id):
print user_id
return User(14, 'root', 'root')
ok,基本使用完成了,前端再写两个简单页面就可以index.html和login.html。代码如下,这里主要理解后端流程,前端能用就可以
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hello, {{ name }}</title>
</head>
<body>
<form name="myform">
<label>用户名:</label>
<input type="text" name="fname"/>
<br>
<label>密码:</label>
<input type="text" name="address"/>
<button type="button" onclick="login()">提交</button>
</form>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script>
function login() {
axios({
method: 'post',
url: '/login',
data: {
username: myform.fname.value,
userpassward: myform.address.value
}
}).then(function (response) {
location.href = response.data
}).catch(function (error) {
console.log(error);
});
} </script> </body>
</html>
login
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
ceshi
<button type="button" onclick="login()">提交</button>
</body>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
{# 发送用户名、密码#}
function login() {
axios({
method: 'post',
url: '/test',
data: {
username: 'c',
userpassward: 'y'
}
}).then(function (response) {
console.log(response.data);
}).catch(function (error) {
console.log(error);
});
}
</script>
</html>
index.html
具体使用过程梳理完成。
再看看其实现的原理,后端路由接收到前端请求后,会进入login_require装饰器中,而此装饰器用来判断用户鉴权情况,如下图:

那用户登录鉴权的关键在装饰器@login_required中,先看下整体的鉴权流程,再深入各个部分,下图为鉴权的整体流程图

此图对应的代码
def login_required(func):
@wraps(func)
def decorated_view(*args, **kwargs):
if current_app.login_manager._login_disabled:
return func(*args, **kwargs)
elif not current_user.is_authenticated:
return current_app.login_manager.unauthorized()
return func(*args, **kwargs)
return decorated_view
login_required
流程解析
1. 判断请求是否需要鉴权,current_app.login_manager._login_disabled,通常的命令get、post等请求都需要鉴权,此属性为False。
2. 判断当前用户是否鉴权,current_user.is_authenticated。current_user--------从哪获取?继续分析代码
current_user = LocalProxy(lambda: _get_user())
current_user通过LoaclProxy创建了一个无名函数_get_user,为什么用lambda:_get_user()而不直接使用_get_user?我也没想明白,有牛人可以解释一下。
LoaclProxy具体可以参考https://www.jianshu.com/p/3f38b777a621,说白了就是理解为把方法地址传给变量,以后可以动态调用代理方法
获取current_user通过_get_user()函数,先看下该函数的代码
def _get_user():
if has_request_context() and not hasattr(_request_ctx_stack.top, 'user'):
current_app.login_manager._load_user() return getattr(_request_ctx_stack.top, 'user', None)
解释一下,首先需要知道请求上下文,代码中的_request_ctx_stack.top中是否有user这个变量,如果没有则_load_user,如果有直接取这个user属性返回给前面的说用来鉴权使用。
_load_user会调用reload_user函数,
def reload_user(self, user=None):
ctx = _request_ctx_stack.top if user is None:
user_id = session.get('user_id')
if user_id is None:
ctx.user = self.anonymous_user()
else:
if self.user_callback is None:
raise Exception(
"No user_loader has been installed for this "
"LoginManager. Add one with the "
"'LoginManager.user_loader' decorator.")
user = self.user_callback(user_id)
if user is None:
ctx.user = self.anonymous_user()
else:
ctx.user = user
else:
ctx.user = user
该函数判断_request_ctx_stack.top赋值user对象,对象可以传入,也可以使用我们在使用过程中定义的def load_user(user_id),这个user_callback就是我们定义的load_user函数。
获取user如图

登入时和重新请求时都会将user放入到栈中,每次请求后都出清理top中的user。放入top时会设置user_id到session中。
flask-login原理详解的更多相关文章
- Flask request 属性详解
Flask request 属性详解 一.关于request在Flask的官方文档中是这样介绍request的:对于 Web 应用,与客户端发送给服务器的数据交互至关重要.在 Flask 中由全局的 ...
- sso单点登录原理详解
sso单点登录原理详解 01 单系统登录机制 1.http无状态协议 web应用采用browser/server架构,http作为通信协议.http是无状态协议,浏览器的每一次请求,服务 ...
- I2C 基础原理详解
今天来学习下I2C通信~ I2C(Inter-Intergrated Circuit)指的是 IC(Intergrated Circuit)之间的(Inter) 通信方式.如上图所以有很多的周边设备都 ...
- Zigbee组网原理详解
Zigbee组网原理详解 来源:互联网 作者:佚名2015年08月13日 15:57 [导读] 组建一个完整的zigbee网状网络包括两个步骤:网络初始化.节点加入网络.其中节点加入网络又包括两个 ...
- 块级格式化上下文(block formatting context)、浮动和绝对定位的工作原理详解
CSS的可视化格式模型中具有一个非常重要地位的概念——定位方案.定位方案用以控制元素的布局,在CSS2.1中,有三种定位方案——普通流.浮动和绝对定位: 普通流:元素按照先后位置自上而下布局,inli ...
- SSL/TLS 原理详解
本文大部分整理自网络,相关文章请见文后参考. SSL/TLS作为一种互联网安全加密技术,原理较为复杂,枯燥而无味,我也是试图理解之后重新整理,尽量做到层次清晰.正文开始. 1. SSL/TLS概览 1 ...
- 锁之“轻量级锁”原理详解(Lightweight Locking)
大家知道,Java的多线程安全是基于Lock机制实现的,而Lock的性能往往不如人意. 原因是,monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖 ...
- [转]js中几种实用的跨域方法原理详解
转自:js中几种实用的跨域方法原理详解 - 无双 - 博客园 // // 这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同 ...
- 节点地址的函数list_entry()原理详解
本节中,我们继续讲解,在linux2.4内核下,如果通过一些列函数从路径名找到目标节点. 3.3.1)接下来查看chached_lookup()的代码(namei.c) [path_walk()> ...
- WebActivator的实现原理详解
WebActivator的实现原理详解 文章内容 上篇文章,我们分析如何动态注册HttpModule的实现,本篇我们来分析一下通过上篇代码原理实现的WebActivator类库,WebActivato ...
随机推荐
- Spring课程 Spring入门篇 1-2Spring简介
课程链接: 1 Spring是什么? 2 为什么是Spring 3 Spring的作用: 4 适用范围 1 Spring是什么? a 开源框架 b 轻量级的控制反转(Ioc)和面向切面编程(AOP)的 ...
- Hibernate课程 初探多对多映射2-1 创建数据库表
--创建表 -- 创建项目表 create table project( proid int(10) not null comment '项目Id', proname varchar(30) co ...
- tween.js 插件
1.是什么? jQueryTween是一款轻量级的jQuery补间动画工具库插件.使用jQueryTween可以制作出各种平滑的动画过渡效果.该插件基于tween.js,旨在简化各种补间动画操作,提供 ...
- RESTful API设计基本原则
REST四个基本原则:1.使用HTTP动词:GET POST PUT DELETE:2.无状态连接,服务器端不应保存过多上下文状态,即每个请求都是独立的:3.为每个资源设置URI:4.通过XML JS ...
- dojo query 基本用法
1. 常用的 dojo.query 用法 dojo.query("#header > h1") //ID 为 header 的元素的直接子节点中的 h3 元素 dojo. ...
- 【起航计划 017】2015 起航计划 Android APIDemo的魔鬼步伐 16 App->Alarm->Alarm Controller Alarm事件 PendingIntent Schedule AlarmManager
Alarm Controller演示如何在Android应用中使用Alarm事件,其功能和java.util.Timer ,TimerTask类似.但Alarm可以即使当前应用退出后也可以做到Sche ...
- 如何找到Android app启动activity和页面元素信息
在实施app自动化的时候,我们需要知道app 的启动activity和页面元素信息,以此启动app和定位页面元素,那么如何在没有源码的情况下找打他们呢?当然是有好的工具啦,有Android sdk自带 ...
- DOM对象和js对象以及jQuery对象的区别
DOM对象和js对象以及jQuery对象的区别 DOM对象和js对象以及jQuery对象的区别 一.DOM对象 文档对象模型简称DOM,是W3C组织推荐的处理可扩展置标语言的标准编程接口. DOM实际 ...
- 笨办法学Python(二十一)
习题 21: 函数可以返回东西 你已经学过使用 = 给变量命名,以及将变量定义为某个数字或者字符串.接下来我们将让你见证更多奇迹.我们要演示给你的是如何使用 = 以及一个新的 Python 词汇ret ...
- avast从隔离区恢复后,仍无法打开被误杀文件的解决方案
从隔离区中手动恢复后,隔离区中被恢复的文件将不再展示. 此时,如果手动恢复的文件仍无法打开(图标此时也异常),请: 将avast禁用: 将avast启用. 然后尝试重新打开被误隔离并手动恢复的文件.