flask_session——RedisSessionInterface 使用
Posted hpython
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了flask_session——RedisSessionInterface 使用相关的知识,希望对你有一定的参考价值。
RedisSessionInterface源码分析
先了解下 请求到来之前,获取session的方式
请求到来之前通过RequestContex 获取session, 由下图看出,open_session 调用session_interface,而session_interface,是SecureCookieSessionInterface()的对象。
而 SecureCookieSessionInterface(),提供了open,和save的方法,所以,可以使用 RedisSessionInterface 替换 SecureCookieSessionInterface,关键就是在配置文件中设置 session_interface指向哪个类
RedisSessionInterface
class RedisSessionInterface(SessionInterface): """Uses the Redis key-value store as a session backend. .. versionadded:: 0.2 The `use_signer` parameter was added. :param redis: A ``redis.Redis`` instance. :param key_prefix: A prefix that is added to all Redis store keys. :param use_signer: Whether to sign the session id cookie or not. :param permanent: Whether to use permanent session or not. """ serializer = pickle #使用pickel方式保存 session_class = RedisSession 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 open_session(self, app, request): # 从cookie中获取session sid = request.cookies.get(app.session_cookie_name) # 首次访问如没有获取到session ID if not sid: # 设置一个随机字符串,使用uuid sid = self._generate_sid() #返回特殊字典 <RedisSession {\'_permanent\': True}> return self.session_class(sid=sid, permanent=self.permanent) #session_class = RedisSession() 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) if val is not None: try: data = self.serializer.loads(val) return self.session_class(data, sid=sid) except: return self.session_class(sid=sid, permanent=self.permanent) return self.session_class(sid=sid, permanent=self.permanent) 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) #用户设置了seesion 后序列化session val = self.serializer.dumps(dict(session)) # key_prefix :用户设置前缀,val 是序列化之后的结果,存入redis 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 # 生成的随机字符串uuid #写入session response.set_cookie(app.session_cookie_name, session_id, expires=expires, httponly=httponly, domain=domain, path=path, secure=secure)
view 中配置RedisSessionInterface 方式一
from flask_session import RedisSessionInterface # from redis import Redis # app.session_interface = RedisSessionInterface(redis=Redis(host=\'127.0.0.1\',port=6379),key_prefix=\'luffi\')
方式二
from flask.ext.session import Session app.config[\'SESSION_TYPE\'] = \'redis\' from redis import Redis app.config[\'SESSION_REDIS\'] = Redis(host=\'192.168.0.94\',port=\'6379\') Session(app)
下面为Session代码:
class Session(object): 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. """ app.session_interface = self._get_interface(app) #这里设置了每次保存/打开session 时都会调用这个self._get_interface(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
SecureCookieSessionInterface —— modified
用户等刚开始登陆时,获取cookie,没有获取到调用session_class ,而session_class 等同于SecureCookieSession。 SecureCookieSession 是一个特殊的字典,继承CallbackDict, SessionMixin,而CallbackDict 继承 UpdateDictMixin, dict ,下面看下UpdateDictMixin代码
"""Makes dicts call `self.on_update` on modifications. .. versionadded:: 0.5 :private: """ on_update = None def calls_update(name): def oncall(self, *args, **kw): rv = getattr(super(UpdateDictMixin, self), name)(*args, **kw) if self.on_update is not None: self.on_update(self) return rv oncall.__name__ = name return oncall def setdefault(self, key, default=None): modified = key not in self rv = super(UpdateDictMixin, self).setdefault(key, default) if modified and self.on_update is not None: self.on_update(self) return rv def pop(self, key, default=_missing): modified = key in self if default is _missing: rv = super(UpdateDictMixin, self).pop(key) else: rv = super(UpdateDictMixin, self).pop(key, default) if modified and self.on_update is not None: self.on_update(self) return rv __setitem__ = calls_update(\'__setitem__\') __delitem__ = calls_update(\'__delitem__\') clear = calls_update(\'clear\') popitem = calls_update(\'popitem\') update = calls_update(\'update\') del calls_update
UpdateDictMixin设置了__setitem__ ,__delitem__,所以当用户设置session时会触发 __setitem__ 方法,调用 calls_updata 方法
calls_update代码如下
def calls_update(name): def oncall(self, *args, **kw): rv = getattr(super(UpdateDictMixin, self), name)(*args, **kw) if self.on_update is not None: self.on_update(self) return rv oncall.__name__ = name return oncall
calls_update。调用了on_update方法
class SecureCookieSession(CallbackDict, SessionMixin): """Base class for sessions based on signed cookies.""" def __init__(self, initial=None): def on_update(self): self.modified = True #调用后设置为True CallbackDict.__init__(self, initial, on_update) self.modified = False
所以当请求结束前,保存session 时执行if not self.should_set_cookie(app, session):
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: response.delete_cookie(app.session_cookie_name, domain=domain, path=path) return 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.get_signing_serializer(app).dumps(dict(session)) response.set_cookie(app.session_cookie_name, val, expires=expires, httponly=httponly, domain=domain, path=path, secure=secure)
should_set_cookie
def should_set_cookie(self, app, session): """Indicates whether a cookie should be set now or not. This is used by session backends to figure out if they should emit a set-cookie header or not. The default behavior is controlled by the ``SESSION_REFRESH_EACH_REQUEST`` config variable. If it\'s set to ``False`` then a cookie is only set if the session is modified, if set to ``True`` it\'s always set if the session is permanent. This check is usually skipped if sessions get deleted. .. versionadded:: 0.11 """ if session.modified: #如果modifed 为True ,则 if not self.should_set_cookie(app, session):就不会return ,继续执行。这样就会更新session return True save_each = app.config[\'SESSION_REFRESH_EACH_REQUEST\'] #每次请求都回会修改session return save_each and session.permanent
根据上面的总结,我们可以确认,前端,如下修改就会更新session
session[\'user_info\'] = {\'k1\':1,\'k2\':2}
如下图这样修改,则不会更改session
session[\'user_info\'][\'k1\'] = 99999
设置 session["modified"] =True 就会更新数值,但是不推荐使用这种方式
session["modified"] =True
推荐使用 ,在settiing中配置
SESSION_REFRESH_EACH_REQUEST= True
注意:使用上面方法需要在初始登录的时候设置 session.permanent = True
@account.route(\'/login\',methods=[\'GET\',"POST"]) def login(): if request.method == \'GET\': form = LoginForm() return render_template(\'login.html\',form=form) form = LoginForm(formdata=request.form) if not form.validate(): return render_template(\'login.html\', form=form) obj = SQLHelper.fetch_one("select id,name from users where name=%(user)s and pwd=%(pwd)s", form.data) if obj: session.permanent = True session[\'user_info\'] = {\'id\':obj[\'id\'], \'name\':obj[\'name\']} return redirect(\'/index\')
使用配置文件进行配置redis
from datetime import timedelta from redis import Redis import pymysql from DBUtils.PooledDB import PooledDB, SharedDBConnection class Config(object): DEBUG = True SECRET_KEY = "umsuldfsdflskjdf" PERMANENT_SESSION_LIFETIME = timedelta(minutes=20) SESSION_REFRESH_EACH_REQUEST= True SESSION_TYPE = "redis" class ProductionConfig(Config): SESSION_REDIS = Redis(host=\'192.168.0.94\', port=\'6379\') class DevelopmentConfig(Config): SESSION_REDIS = Redis(host=\'127.0.0.1\', port=\'6379\') class TestingConfig(Config): pass
from s8pro_flask import create_app app = create_app() if __name__ == \'__main__\': app.run()
from flask import Flask from flask_session import Session from .views import account from .views import home def create_app(): app = Flask(__name__) app.config.from_object(\'settings.DevelopmentConfig\') app.register_blueprint(account.account) app.register_blueprint(home.home) # 将session替换成redis session Session(app) return app
以上是关于flask_session——RedisSessionInterface 使用的主要内容,如果未能解决你的问题,请参考以下文章