Flask-Login为Flask提供了用户会话管理,它处理了日常的登入登出且长时间记住用户的会话
使用:
1、配置,初始化 LoginManager
创建实例
loginManger = LoginManager()
绑定到核心app
loginManager.init_app(app)
2、我们的User Model需要需要实现下面这些属性和方法:
is_authenticated:用户通过验证时,会返回True(只有通过验证的用户会满足login_required的条件)
is_active:账户处于激活状态的情况下,会返回True
is_anonymous:是否是匿名用户,是就返回True,真是用户返回False
get_id():返回一个唯一识别用户的id
flask-login的UserMixin已经都实现了这些属性和方法,所以我们在定义我们的User Model的时候,直接继承UserMixin即可。
3、用户的用户名密码验证通过之后,我们可以使用login_user(user)来使用户登录
它会创建一个token,写到浏览器的cookie中
然后在我们访问任何页面时,flask-login会检查是否登录,没有登录的话,会跳转到登录页
登录成功,会重定向到我们指定的页面,这里,我们还需要注意,一定要做重定向地址验证,否则很容易就受到重定向攻击:
如我们在login的时候输入如下url:http://127.0.0.1:5000/login?next=http://www.baidu.com
那么在登录成功后,就会直接跳转到百度界面
4、user_loader的使用
我们必须要提供这个user_loader的回调函数,它用户通过会话中的sessionid重新加载用户对象。
1、在login_user内部,我们将user的属性写入到session中,session["user_id"] = user.id,并将user绑定到请求上下文
2、用户发起一次http请求的时候,如果ctx.user无值,会通过某个callback查询当前登录的用户,这个callback的参数就是user_id,即session["user_id"]
3、loginManager.user_loader就是注册这个callback
4、此回调函数,其实就是用来记录用户的登录状态的。
5、下面看看整个登录过程:
1)首先前端用户post过来用户名密码,我们在View中接收并验证正确后,会调用login_user执行登录,并将token写入到前端cookie中
我们去函数内部看看login_user的源码,这里它将用户信息user_id 写入到了session["user_id"]中,并将user绑定到请求上下文中。
由于http是无状态连接,每次发起新的请求时,flask会创建一个新的请求上下文,在分发路由时,flask-login根据cookie判断用户并绑定的请求上下文,由于这种绑定关系的存在,新的请求发生时,都需要获取user。
(flask-login--->util.py)
再看我们的reload_user(login_manager.py),首先会从session中获取user_id,如果user_id存在,通过参数user_id去调用回调函数,也就是我们通过loginManager.user_loader绑定的回调函数,去查找当前登录的user,并将其绑定到当前的请求上下文
绑定的意义就在于每次当我们使用current_user的时候,会直接从当前上下文中返回。
如果当前session中没有user_id,那么会认为当前没有登录,会返回None
6、上面几步都走完了,我们就登录成功了。
7、验证
对于需要用户登录才能执行的,可以在视图函数上加上装饰器 login_required
默认情况下,当未登录用户访问一个login_required装饰器装饰的视图时,Flask-Login会闪现一条消息并重定向到一个登录视图
app.router("/setting")
@login_required
def settings():
pass
8、登出
当用户要登出时
app.router("/logout")
@login_required
def logout():
logout_user()
return redirect("")
此时会登出,且原先登录的各种会话的cookie都会被清除。
9、自定义未授权登录
如果未登入用户访问了login_required装饰的视图函数,Flask-Login默认会给我们返回401 unauthorized
从源码中我们可以看到,其实我们是可以自定义当用户无权限访问时的处理的:
1)我们可以自定义login_view
loginManager.login_view = "web.login"
loginManager.login_message = "please login first"
2)我们可以重写unauthorized_callback函数,使用unauthorized_handler装饰
10、前面使用的是user_loader来检查登录,它是通过使用cookie携带登录的token来完成登录,那如果我们不想使用cookie的方式来完成登录呢,比如使用HTTP头,或者一个作为查询参数的api秘钥?
这种情况下,我们应该使用Request Loader回调来定制登录检查,request loader接受的是Flask请求request,不再是user_id
11、记住我
默认情况下,当用户关闭浏览器的时候,当前会话的cookie就会被删除。但是我们在使用浏览器的时候,经常会看到“记住我” 的操作,记住我可以防止用户在关闭浏览器时意外退出。
这并不意味着用户注销后记住或预先填写登录表单中的用户名和密码。
在flask-login中,这项看似很复杂的功能,使用起来很简单,只需要将remember=True传递给login_user即可。
cookie将被保存在用户的计算机上,如果不在会话中,Flask-login将自动从该cookie中恢复用户ID,cookie到期前的时间可以通过 REMEMBER_COOKIE_DURATION配置进行设置,也可以通过login_user进行设置。
12、通过token认证
使用token来替代存放用户信息到session中,这样具有更多的灵活性
@login_manager.user_loader
def load_user(session_token):
return User.query.filter_by(session_token = session_token).first()
如果使用token来验证,需要改变user的get_id方法:
def get_id(self):
return unicode(self.session_token)
这样用户在修改密码的时候,可以自由的将会用的会话标识更新为新的随机字符串,旧的认证会话将不再有效。token必须唯一。
13、刷新登录