flask_login 设置session_protection='strong'

使用flask_login扩展时,我们使用login_user() 将当前登陆用户设置为登陆,并保存,关闭浏览器再打开网页时,能自动加载用户。可是当session_protection=’strong’ 关闭浏览器后再次打开网页用户并没有被加载,而将session_protection设为basic或者不设置session_protection则不会出现这个问题。通过查看flask_login和flask中session部分的源码就知道为什么了。当我们使用login_user()时,需要传入当前登录的用户对象,如果想记住用户方便用户以后访问,可以传入参数remember=True 。在调用这个函数后,flask_login 会将一些信息存到session中,这些信息包括user_id _fresh _id ,请求完成后这些值就会被保存在用户的浏览器中,但是我们不在调用login_user后,设置session.permanent=True,那么刚才存在session 中的信息(即存储在浏览器中cookie的信息)在浏览器关闭后就会失效。无论是current_user 还是 装饰器login_require ,都会在从cookie加载用户时调用_load_user,而此时浏览器中已经没有session对应的cookie,我们看一下内部是如何处理的

    def _load_user(self):
        '''Loads user from session or remember_me cookie as applicable'''
        user_accessed.send(current_app._get_current_object())

        # first check SESSION_PROTECTION
        config = current_app.config
        if config.get('SESSION_PROTECTION', self.session_protection):
            deleted = self._session_protection()
            if deleted:
                return self.reload_user()
    def _session_protection(self):
        sess = session._get_current_object()
        ident = self._session_identifier_generator()

        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

由于我们设置了session_protection所以会执行self._session_protection(),在这个函数里如果sess存在并且 ident != sess.get(‘_id’, None) (这里可以确定sess肯定存在,并不是浏览器中没有session对应的cookie,sess就不存在。事实上,只要配置了secret_key 这里的sess就肯定不为空,这是flask 在push 上下文中就决定的) ,我们上面说过,浏览器中的session对应的cookie已经没有了,ident != sess.get(‘_id’, None)肯定为false (ident 是根路浏览器的user_agent和你的ip地址生成的字符串),继续往下执行,因为我们设置的是strong 所有,session 中的所有key都被pop出,也就是session现在为空,继续往回看,要执行return self.reload_user()

        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. Refer to"
                        "https://flask-login.readthedocs.io/"
                        "en/latest/#how-it-works for more info.")
                user = self.user_callback(user_id)
                if user is None:
                    ctx.user = self.anonymous_user()
                else:
                    ctx.user = user
        else:
            ctx.user = user

首先看上下文中有没有用户,现在肯定没有,然后从session中取user_id ,前面已经都pop完了,肯定也没有,所有将上下文中的用户设置为匿名用户。这就解释了开头提到的现象。可见要想在strong模式下,使用remember功能,必须设置session.permanent=True。如果在调用login_user()时,传入了remember=True,你会发现不设置session.permanent=True ,它也不会丢失,这是因为flask_login 在保存remember时和我们上面提到的user_id _fresh _id 采取了不同的方法,后面几个值是直接存在flask中的session中,然后存到浏览器的cookie中,而remember虽然在开始时也是存在了session中,但是flask_login 定义了一个after_request函数,在请求结束后如果session中有remember,就pop出,然后直接使用set_cookie方法设置cookie ,并且如果你没有设置REMEMBER_COOKIE_DURATION,那么remember会默认一年有效。
我们再来看一下将session_protection分别设置为basic和strong 具体有哪些区别:在调用login_user()时,不设置session.permanent , mode=basic 在cookie中的session被篡改或者用户ip发生变化时 则会从设置在cookie中的remember_token 加载用户但会把用户标记为‘不新鲜’,但是strong模式,则会直接返回匿名用户。

    def _session_protection(self):
        sess = session._get_current_object()
        ident = self._session_identifier_generator()

        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 设置session_protection='strong')