码迷,mamicode.com
首页 > 其他好文 > 详细

flask_session——RedisSessionInterface 使用

时间:2018-04-30 00:58:59      阅读:183      评论:0      收藏:0      [点我收藏+]

标签:param   cas   was   访问   views   nec   图片   ups   not   

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
setting
技术分享图片
from s8pro_flask import create_app
app = create_app()

if __name__ == __main__:
    app.run()
manage.py
技术分享图片
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
__init__.py

 

flask_session——RedisSessionInterface 使用

标签:param   cas   was   访问   views   nec   图片   ups   not   

原文地址:https://www.cnblogs.com/huyangblog/p/8971353.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!