Flask

Flask-Session

1. Flask-Session 源码一瞥

  • Session类实例化
    #在app.py中实例化
    Session(app)
  • 实例化后执行__new__方法,然后执行__init__方法
     def __init__(self, app=None):
            self.app = app
            if app is not None:
                self.init_app(app)
    
        def init_app(self, app):
            """This is used to set up session for your app object.
            :param app: the Flask app object with proper configuration.
            """
            #执行 _get_interface 获取session接口
            app.session_interface = self._get_interface(app)

  • 执行 _get_interface 方法
       def _get_interface(self, app):
            config = app.config.copy()   # 浅拷贝,此时app.config并没有写相关的东西
            config.setdefault('SESSION_TYPE', 'null')            #设置默认值
            config.setdefault('SESSION_PERMANENT', True)
            config.setdefault('SESSION_USE_SIGNER', False)
            config.setdefault('SESSION_KEY_PREFIX', 'session:')
            config.setdefault('SESSION_REDIS', None)            #redis好用
            config.setdefault('SESSION_MEMCACHED', None)       #可以忽略以前使用,支持数据类型少,不支持持久化,redis比他好
            config.setdefault('SESSION_FILE_DIR',
                              os.path.join(os.getcwd(), 'flask_session'))
            config.setdefault('SESSION_FILE_THRESHOLD', 500)
            config.setdefault('SESSION_FILE_MODE', 384)
            config.setdefault('SESSION_MONGODB', None)
            config.setdefault('SESSION_MONGODB_DB', 'flask_session')
            config.setdefault('SESSION_MONGODB_COLLECT', 'sessions')
            config.setdefault('SESSION_SQLALCHEMY', None)
            config.setdefault('SESSION_SQLALCHEMY_TABLE', 'sessions')
    
            if config['SESSION_TYPE'] == 'redis':
                session_interface = RedisSessionInterface(
                    config['SESSION_REDIS'], config['SESSION_KEY_PREFIX'],
                    config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
            elif config['SESSION_TYPE'] == 'memcached':
                session_interface = MemcachedSessionInterface(
                    config['SESSION_MEMCACHED'], config['SESSION_KEY_PREFIX'],
                    config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
            elif config['SESSION_TYPE'] == 'filesystem':
                session_interface = FileSystemSessionInterface(
                    config['SESSION_FILE_DIR'], config['SESSION_FILE_THRESHOLD'],
                    config['SESSION_FILE_MODE'], config['SESSION_KEY_PREFIX'],
                    config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
            elif config['SESSION_TYPE'] == 'mongodb':
                session_interface = MongoDBSessionInterface(
                    config['SESSION_MONGODB'], config['SESSION_MONGODB_DB'],
                    config['SESSION_MONGODB_COLLECT'],
                    config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
                    config['SESSION_PERMANENT'])
            elif config['SESSION_TYPE'] == 'sqlalchemy':
                session_interface = SqlAlchemySessionInterface(
                    app, config['SESSION_SQLALCHEMY'],
                    config['SESSION_SQLALCHEMY_TABLE'],
                    config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
                    config['SESSION_PERMANENT'])
            else:
                session_interface = NullSessionInterface()
    
            return session_interface
  • 当未设置app.config["SESSION_TYPE"] 执行  NullSessionInterface()
    class NullSessionInterface(SessionInterface):
        """Used to open a :class:`flask.sessions.NullSession` instance.
        """
        def open_session(self, app, request):
            return None
    
    #所以当未设置config["SESSION_TYPE"],时候 open_session 返回None

    注: 未设置 app.config["SESSION_TYPE"] 时,此时在flask项目中默认生效的还是内置的session,此时仍然需要secret_key配置

  • 若是对 app.config["SESSION_TYPE"] 配置以及对相应的redis进行配置则:在_get_interface 方法中执行相应的内容
    app.config["SESSION_TYPE"] = "redis"
    #在任何情况下都要将redis放到内网,不允许暴露到公网
    app.config["SESSION_REDIS"] = Redis(host="127.0.0.1",port=6379)
    #将cookie name 修改为其他名字
    app.config["SESSION_COOKIE_NAME"] = "HEHE"
    
    Session(app)

    实例化的  RedisSessionInterface 对象 __init__方法

        def __init__(self, redis, key_prefix, use_signer=False, permanent=True):
            if redis is None:
                from redis import Redis
                redis = Redis()# 创建redis连接,此时第一传进来的是一个redis连接
            self.redis = redis
            self.key_prefix = key_prefix
            self.use_signer = use_signer
            self.permanent = permanent
  • 当认证成功够通过open_session 会从session中取到对应值即从cookies中获取值
        def open_session(self, app, request):
            #取出session
            sid = request.cookies.get(app.session_cookie_name)
            if not sid:
                sid = self._generate_sid()
                return self.session_class(sid=sid, permanent=self.permanent)
            if self.use_signer:  #False
                signer = self._get_signer(app)
                if signer is None:
                    return None
                try:
                    sid_as_bytes = signer.unsign(sid)
                    sid = sid_as_bytes.decode()
                except BadSignature:
                    sid = self._generate_sid()
                    return self.session_class(sid=sid, permanent=self.permanent)
    
            if not PY2 and not isinstance(sid, text_type):
                sid = sid.decode('utf-8', 'strict')
            val = self.redis.get(self.key_prefix + sid)   #session:    f95dbedd-783c-4cd3-9cdf-54f11c9790e0
            if val is not None:
                try:
                    #序列化解包
                    data = self.serializer.loads(val)
                    return self.session_class(data, sid=sid) #session_class 将session以字典的形式返回
                except:
                    return self.session_class(sid=sid, permanent=self.permanent)
            return self.session_class(sid=sid, permanent=self.permanent)
  • 通过save_session进行存储
            if login_form_data.validate():
                session["user"] = login_form_data.data.get("username")
                # 此时进过save_session方法,此时以字典的形式存储,通过键值"user"取值
                return str(session["user"])
       def save_session(self, app, session, response):
            domain = self.get_cookie_domain(app)
            path = self.get_cookie_path(app)
            if not session:
                if session.modified:
                    self.redis.delete(self.key_prefix + session.sid)
                    response.delete_cookie(app.session_cookie_name,
                                           domain=domain, path=path)
                return
    
            # Modification case.  There are upsides and downsides to
            # emitting a set-cookie header each request.  The behavior
            # is controlled by the :meth:`should_set_cookie` method
            # which performs a quick check to figure out if the cookie
            # should be set or not.  This is controlled by the
            # SESSION_REFRESH_EACH_REQUEST config flag as well as
            # the permanent flag on the session itself.
            # if not self.should_set_cookie(app, session):
            #    return
    
            httponly = self.get_cookie_httponly(app)
            secure = self.get_cookie_secure(app)
            expires = self.get_expiration_time(app, session)
            val = self.serializer.dumps(dict(session))
            #以字典的形式强制打包
            self.redis.setex(name=self.key_prefix + session.sid, value=val,
                             time=total_seconds(app.permanent_session_lifetime))
            if self.use_signer:
                session_id = self._get_signer(app).sign(want_bytes(session.sid))
            else:
                session_id = session.sid
            response.set_cookie(app.session_cookie_name, session_id,
                                expires=expires, httponly=httponly,
                                domain=domain, path=path, secure=secure)
  • 修改session_cookies_name
    #将cookie name 修改为其他名字
    app.config["SESSION_COOKIE_NAME"] = "HEHE"
原文地址:https://www.cnblogs.com/wcx666/p/10447095.html