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"