使用Flask
作为工作的主要框架也快两年了,对于Flask-Login
插件一直是似懂非懂,今天决定参考一些文章资料深入理解下原理。
在深入学习原理前我们需要知道如何在我们的Flask
项目中集成Flask-Login
插件,具体可以参考官方文档或者之前的一篇笔记Flask基础学习-flask-login篇
flask-login
一些知识点:
login_user 函数
我们在使用Flask-Login
插件的时候,登录使用的函数是login_user
函数。我们先看下这个函数的源码:
1 | def login_user(user, remember=False, duration=None, force=False, fresh=True): |
我们分析一下这个函数具体做了什么。
函数的第一个参数是用户模型User
的实例。
之前在配置使用Flask-Login
的时候我们在用户模型上面继承了UserMixin
。
这样我们可以通过get_id
方法获得到user
实例的id
。
然后将获得user_id
保存到session
中,除此之外将更新_fresh
重新生成会话id
。
如果我们设置了remember
和duration
则会进行相应的更新。
然后将当前用户推入到一个线程隔离的请求栈的栈顶,这样使用login_required
获得的就是这个栈顶的user
。
最后发出登录信号,调用了_get_user
1 | def _get_user(): |
在这个函数中判断_request_ctx_stack
中是否存在user
对象
如果没有则调用LoginManager
的_load_user
方法。
如果有则结束登录流程,然后进入到_update_remember_cookie
函数中。
为什么是这个函数呢?我看了下LoginManager
的源码中的初始化方法中有指定请求钩子。
1 | def init_app(self, app, add_context_processor=True): |
1 |
|
在_update_remember_cookie
函数中,如果设置了remember
则会写入cookie
否则不处理。
注意:通过上面函数我们看到即使没有设置remember
,浏览器也会被写入一段cookie
。这个cookie
其实是flask
的session
。flask
的session
实现很特殊,它不保存在服务器,它保存在浏览器的cookie
中。
关于 current_user的原理
1 | current_user = LocalProxy(lambda: _get_user()) |
current_user
是一个local proxy
,被代理的函数是_get_user
。当我们调用current_user
的时候,会实时的调用_get_user
方法。这个函数源码在上面,在方法内部会去查看_request_ctx_stack
中是否有user
对象,如果有则返回,如果没有则调用_load_user
方法,这个返回回去cookie
或者session
中加载user
对象。
1 | def user_loader(self, callback): |
我们看到_load_user
最终调用的是reload_user
的返回。这个函数才是真正获得用户对象的函数。我们一般重写这个函数,在我们集成Flask-Login
的时候。
1 |
|
完成这个配置之后,源码里所有的self.user_callback
都是指代我们配置的这个函数。
关于装饰器 login_require的实现
我们看下这个装饰器的源码
1 | def login_required(func): |
我们看到装饰器中使用了current_user
,一旦使用了current_user
,就会触发_get_user
方法,一旦触发这个方法后续流程就会和current_user
的原理分析一样了。
最后看下 logout_user 函数
1 | def logout_user(): |
logout_user
主要是清除了session
和cookie
中的关键参数。比如login
时设置的user_id
以及remember
等,清除后又调用了reload_user()
,根据之前的逻辑,当然不可能重载成功,因为user_id
已经为None
了,执行到ctx.user = self.anonymous_user()
就已经结束了
关于session的问题
flask
的session
默认是client side
的。也就是说flask
的session
是写入到浏览器cookie
中的。当浏览器关闭后,默认情况下,这个cookie
会丢失。
flask-login 的 remember me 功能
我们可以通过将flask
的session
设置为长期session
来实现记住用户的功能。但是这样一来所有的session
都将被设置为长期session
。flask-login
通过设置一个单独的cookie
来实现记住我。但是这个单独的cookie
并不会直接被使用,而是同样要搭配flask
的session
来实现。如果session
中没有userid
,那么flask
会从flask-login
设置的cookie
中恢复userid
并存储到session
中。
参考文章: