情景:

Flask-Login从0.2.11升级到0.3.2

使用Flask-Login的登录系统,login_manager.session_protection 设置为 ‘strong’

1.设置remember=True,登录系统,可以得到 remember_token

登录后的cookies

2.关闭浏览器,重新访问: remember_token被清除

第一次访问

3.刷新页面,显示未登录

第二次访问

错误内容:

第二次打开浏览器,Flask-Login 没有记住用户登录状态

原因:

登录系统,Flask-Login会将一个token记录到session里,以标记这个用户是“新鲜”的(在浏览过程中输入过密码)

当浏览器关闭,session被删除,第二次登录系统时

如果session_protection设置为strong,Flask-Login会先检测是session是否有这个token,这是肯定没有的,所以就删除了记住我的cookies

如果设置为basic,Flask-Login仅仅将用户标记为“不新鲜的”,然后通过cookies登录

Flask-Login 0.3.2的session保护代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def _session_protection(self):
sess = session._get_current_object()
ident = _create_identifier()

app = current_app._get_current_object()
mode = app.config.get('SESSION_PROTECTION', self.session_protection)

# if the sess is empty, it's an anonymous user or just logged out
# so we can skip this

if sess and ident != sess.get('_id', None):
if mode == 'basic' or sess.permanent:
sess['_fresh'] = False
session_protected.send(app)
return False
elif mode == 'strong':
for k in SESSION_KEYS:
sess.pop(k, None)

sess['remember'] = 'clear'
session_protected.send(app)
return True

return False

Flask-Login 0.2.11好像不会进入到删除cookies和设置为“不新鲜”那步,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def _session_protection(self):
sess = session._get_current_object()
ident = _create_identifier()

app = current_app._get_current_object()
mode = app.config.get('SESSION_PROTECTION', self.session_protection)

# if there is no '_id', then take the current one for good
if '_id' not in sess:
sess['_id'] = ident

# if the sess is empty, it's an anonymous user, or just logged out
# so we can skip this, unless 'strong' protection is active,
# in which case we need to double check for the remember me token
check_protection = sess or mode == 'strong'

if check_protection and ident != sess.get('_id', None):
if mode == 'basic' or sess.permanent:
sess['_fresh'] = False
session_protected.send(app)
return False
elif mode == 'strong':
sess.clear()
sess['remember'] = 'clear'
session_protected.send(app)
return True

return False

解决方法:

login_manager.session_protection 设置成默认值 basic , 或者不设置