一:简介
flask-session是flask框架的session组件,由于原来flask内置session使用签名cookie保存,如果我们在session中不存敏感信息还好,如果存的是敏感信息那么信息安全是没有保障的,该flask_session组件则将支持session保存到多个地方,如:
- redis
- memcached
- filesystem
- mongodb
- sqlalchmey
二:案例
1:安装
pip3 install Flask-Session
2:配置session存储到redis中
服务端
from flask import Flask,session from flask_session import Session from redis import Redis app = Flask(__name__) app.config['SESSION_TYPE'] = 'redis' # session类型为redis app.config['SESSION_PERMANENT'] = False # 如果设置为True,则关闭浏览器session就失效。 app.config['SESSION_USE_SIGNER'] = False # 是否对发送到浏览器上session的cookie值进行加密 app.config['SESSION_KEY_PREFIX'] = 'session:' # 保存到session中的值的前缀 app.config['SESSION_REDIS'] = Redis(host='127.0.0.1',port=6379,db=6) # 用于连接redis的配置 Session(app) @app.route('/') def index(): session['user'] = 'hi' return 'hello' if __name__ == '__main__': app.run(debug=True)
3:浏览器访问服务端查看是否有session
4:查看redis缓存中是否有redis
三:源码解析
请先查看 上下文源码
1:设置 session_interface
class Session(object): def __init__(self, app=None): self.app = app if app is not None: self.init_app(app) def _get_interface(self, app): config = app.config.copy() #浅拷贝 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) config.setdefault('SESSION_MEMCACHED', None) 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
class RedisSessionInterface(SessionInterface):
def __init__(self, redis, key_prefix, use_signer=False, permanent=True): if redis is None: from redis import Redis redis = Redis() self.redis = redis self.key_prefix = key_prefix self.use_signer = use_signer self.permanent = permanent
def __init__(self, host='localhost', port=6379, db=0, password=None, socket_timeout=None, socket_connect_timeout=None, socket_keepalive=None, socket_keepalive_options=None, connection_pool=None, unix_socket_path=None, encoding='utf-8', encoding_errors='strict', charset=None, errors=None, decode_responses=False, retry_on_timeout=False, ssl=False, ssl_keyfile=None, ssl_certfile=None, ssl_cert_reqs='required', ssl_ca_certs=None, max_connections=None):
。。。。。。。。
2:执行上下文时获取到session并存入到redis中
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
def push(self):
if self.session is None:
session_interface = self.app.session_interface #这里的session_interface是被浅拷贝了,所以上面得到的session_interface和这里的是一个空间
self.session = session_interface.open_session( #这里的session_interface中的open_session()就执行的是下面的
self.app, self.request
)
if self.session is None:
self.session = session_interface.make_null_session(self.app)
拿到cookie中的session
class RedisSessionInterface(SessionInterface): def open_session(self, app, request): sid = request.cookies.get(app.session_cookie_name) #这个是app.config源码中的那个session_cookie_name ,里面是session if not sid: #从cookie中获取的session id sid = self._generate_sid() return self.session_class(sid=sid, permanent=self.permanent) if self.use_signer: 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:6a899d9e-c5ac-49a6-b43e-f5fb0044bb2e if val is not None: try: data = self.serializer.loads(val) #{key:value} return self.session_class(data, sid=sid) #获取的session替换原来放session的那个偏函数 except: return self.session_class(sid=sid, permanent=self.permanent) return self.session_class(sid=sid, permanent=self.permanent)
把session中存放在redis中
def save_session(self, app, session, response): domain = self.get_cookie_domain(app) path = self.get_cookie_path(app)
。。。。。。。
self.redis.setex(name=self.key_prefix + session.sid, value=val,
time=total_seconds(app.permanent_session_lifetime))
。。。。。。。