使用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中。
参考文章: