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

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

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

时间: 2024-11-06 13:29:56

flask_session——RedisSessionInterface 使用的相关文章

Flask_Session插件

一,介绍 因为flask自带的session是将session存在cookie中: 所以才有了第三方Flask_session插件,可以将session存储在我们想存储的数据库中(redis等) 二,使用 pip install Flask-Session from flask import Flask, request, render_template, redirect, session from flask_session import Session import redis app =

Flask之Flask_Session插件

一,Flask_Session介绍 因为flask自带的session是将session存在cookie中: 所以才有了第三方Flask_session插件,可以将session存储在我们想存储的数据库中(redis等) 二,使用 首先需要安装一下Flask_session pip install Flask-Session from flask import Flask, request, render_template, redirect, session from flask_sessio

flask flask_session,WTForms

一.Flask_session 本质上,就是是cookie 下的session存储在redis中,方便快速取得session from flask import Flask,session from flask_session import Session from redis import Redis app=Flask(__name__) #这两个是必须填写的,在session源码中, ''' if config['SESSION_TYPE'] == 'redis': session_int

Flask快速入门(17) — flask_session

Flask快速入门(17) - flask_session 作用:将默认保存的签名cookie中的值,保存到 redis/memcached/file/Mongodb/SQLAlchemy 安装:pip install flask-session 使用1: from flask import Flask,session from flask_session import RedisSessionInterface import redis app = Flask(__name__) conn=r

Flask组件 flask_session

flask-session是flask框架的session组件,由于原来flask内置session使用签名cookie保存,该组件则将支持session保存到多个地方,如: redis memcached filesystem mongodb sqlalchmey 安装 pip3 install flask-session 存储方式 redis #!/usr/bin/env python # -*- coding:utf-8 - import redis from flask import F

flask_session

flask_session和Flask中的session相比,比较简单,省去了 secret_key 首先,导入flask_session 模块  from flask_session import Session     ,然后利用  Session(app)  来替换原来session的机制 代码如下: from flask import Flask,session from flask_session import Session from redis import Redis app =

Flask中session源码执行过程

1.面向对象补充知识metaclass 创建类就可以有两种方式: a.普通方式 1 class Foo(object): 2 3 def func(self): 4 print 'hello wupeiqi' b.特殊方式(type类的构造函数) 1 def func(self): 2 print 'hello wupeiqi' 3 4 Foo = type('Foo',(object,), {'func': func}) 5 #type第一个参数:类名 6 #type第二个参数:当前类的基类

Flask基础(3):session、flash、特殊装饰器、蓝图、路由正则匹配、上下文管理 &amp; flask-session

Session: Flask 默认将 session 以加密的形式放到了浏览器的 cookie 中 Flask 的 session 就是一个字典,字典有什么方法 session 就有什么方法 flask session 原理:当请求刚进来时,flask会读取cookie中 session(配置文件中能改这个名称) 对应的值,将这个值解密并反序列化成为一个字典放入内存, 以便视图函数使用: 在视图函数中使用时,按照字典的方法使用: 当请求结束时,flask会读取内存中字典的值,进行序列化+加密,然

1.4 flask request和session

2019-1-4 18:13:57 越努力,越幸运! 还有121天,flask讲完,还有4天,总时长130天 还有13天就讲完了 一月争取看完!!! 永远不要高估自己! 今天学了request和session的上下文管理 其实就是如图所示 流程图 flask只是把请求相关东西放在某个神奇的地方,当你需要的时候就去那边拿去 然后用上下文管理去拿 贴上源码 面向对象中特殊的方法 class Foo(object): def __init__(self): # self.storage = {} ob