Flask之flask_session

flask原本的session是保存在浏览器cookie中的,这样就产生了一个很重要的问题,如果我们在session中不存敏感信息还好,如果存的是敏感信息那么信息安全是没有保障的,而flask_session可以让我们把session的值存储在redis/Memcached中。

将数据存储在redis/memcached中,然后把所对应的key存放在用户的cookie中。

实现方式:
  在flask源码中

def wsgi_app(self, environ, start_response):
    ctx = self.request_context(environ)
        ctx.push()
        error = None
        try:
            try:
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)

wsgi_app
ctx.push()方法中有

        if self.session is None:
            session_interface = self.app.session_interface
            self.session = session_interface.open_session( self.app, self.request)

            if self.session is None:
                self.session = session_interface.make_null_session(self.app)

执行的是 session_interface.open_session( self.app, self.request)

这里的session_interface是flask中默认的,所以我们如果把默认的session_interface重新赋值那么open_session执行的就不会是flask中默认的session_interface,而应该是我们重新赋值的类中的open_session.

比如我们在视图app.py中配置了以下代码:

from flask_session import Session
from redis import Redis

app=Flask(__name__)

app.config["SESSION_TYPE"]="redis" #设置使用redis存session
app.config["SESSION_REDIS"]=Redis(host='127.0.0.1',port=6379)# 建立Redis链接,这个也可以不配置,内部会自己创建链接Redis()

app.config["SESSION_COOKIE_NAME"]='vndvn'#设置session在浏览器中的key的值  默认是'session'

Session(app) 

那么Session(app) 在初始化的时候

 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):
        app.session_interface = 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'])
       ...这里可以忽略...
        return session_interface

这里的session_interface就是RedisSessionInterface的实例化对象,open_session方法也就是RedisSessionInterface中的open_session方法:

 def open_session(self, app, request):
        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:
            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)

RedisSessionInterface.open_session源码解析:

1.先从cookie中拿值,并赋值给sid.

2.判断是否cookie中sid是否存在

3.1如果不存在那么就返回一个特殊的字典

3.2如果有sid,就去redis中拿值,并用pickle反序列化,返回一个字典

当我们在视图函数中进行session操作的时候会调用RedisSessionInterface.save_session,我们接着来看看save_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:
                self.redis.delete(self.key_prefix + session.sid)
                response.delete_cookie(app.session_cookie_name,
                                       domain=domain, path=path)
            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:  #默认use_signer=False
            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)

拿到域名,和路径如果有路径判断是否有修改,此处会出现一个bug就是你在修改数据的时候,程序只会检测到你的第一级数据有没有变化,而不会检测到内部的字典是否发生了变化。

这时我们可以在views中self.session[‘modified’] = True

但是我们更加推荐另一种方式在配置文件中设置

SESSION_REFRESH_EACH_REQUEST = True
这样有一个好处每次都可以设置session而且可以刷新session的过期时间,在原生的session_interface中还要设置

session.permanent = True

而在我们设置的flask_session中就不用设置了默认为True

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值