博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
flask源码解读06: Session(会话)
阅读量:5260 次
发布时间:2019-06-14

本文共 6440 字,大约阅读时间需要 21 分钟。

Flask中Session的实现,关于session和cookie的原理读者可查阅其他资料,我的理解如下:

服务器会为每个新到来的连接创建一个sessionID,以此识别各个连接,另外服务器可以根据sessionID来找到保存在服务器端的这个连接之前保存的数据。这个功能有两个好处:将数据保存在服务器端更安全,相比客户端而言更不容易被攻击;二是由于http协议是无状态协议,即后一个连接不会带有前一个连接的任何信息。但如果有session,后面的连接可以在被处理之前,找到存放在服务器中的数据。那么每个连接的sessionID如何保存,它被保存在客户端的cookie中。cookie中存放的相当于一把钥匙,这把钥匙可以打开存放在服务器的东西。

 

在flask中,请求环境入栈的时候,有下面的代码

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为空,会根据请求和app中的信息创建session对象,如果创建失败会创建一个空的session

class SecureCookieSessionInterface(SessionInterface):    """The default session interface that stores sessions in signed cookies    through the :mod:`itsdangerous` module.    """    #: the salt that should be applied on top of the secret key for the    #: signing of cookie based sessions.    salt = 'cookie-session'    #: the hash function to use for the signature.  The default is sha1    digest_method = staticmethod(hashlib.sha1)    #: the name of the itsdangerous supported key derivation.  The default    #: is hmac.    key_derivation = 'hmac'    #: A python serializer for the payload.  The default is a compact    #: JSON derived serializer with support for some extra Python types    #: such as datetime objects or tuples.    serializer = session_json_serializer    session_class = SecureCookieSession    def get_signing_serializer(self, app):        if not app.secret_key:            return None        signer_kwargs = dict(            key_derivation=self.key_derivation,            digest_method=self.digest_method        )        return URLSafeTimedSerializer(app.secret_key, salt=self.salt,                                      serializer=self.serializer,                                      signer_kwargs=signer_kwargs)    def open_session(self, app, request):        s = self.get_signing_serializer(app)        if s is None:            return None        val = request.cookies.get(app.session_cookie_name)        if not val:            return self.session_class()        max_age = total_seconds(app.permanent_session_lifetime)        try:            data = s.loads(val, max_age=max_age)            return self.session_class(data)        except BadSignature:            return self.session_class()    def save_session(self, app, session, response):        domain = self.get_cookie_domain(app)        path = self.get_cookie_path(app)        # If the session is modified to be empty, remove the cookie.        # If the session is empty, return without setting the cookie.        if not session:            if session.modified:                response.delete_cookie(                    app.session_cookie_name,                    domain=domain,                    path=path                )            return        # Add a "Vary: Cookie" header if the session was accessed at all.        if session.accessed:            response.vary.add('Cookie')        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        )

看上面代码中open_session方法,先根据app创建一个序列化对象s,然后查看request.cookies.get(app.session_cookie_name)是否存在,这个值就类似我们前面说的sessionID,如果值存在且通过s.loads方法反序列化成功,则return self.session_class(data),返回用data初始化的session实例。如果值不存在或者反序列化失败,都是return self.session_class()返回没有初始化的session实例。

注意flask此处储存用户session数据的方法简化了,前面说的服务器会储存用户的session数据,一般是用数据库存储。但在flask中,数据序列化后直接存储在用户端的cookie中,不过序列化数据能提供一定的保密性。

 

self.session_class(data)用到的session类是SecureCookieSession

class SecureCookieSession(CallbackDict, SessionMixin):    """Base class for sessions based on signed cookies."""    def __init__(self, initial=None):        def on_update(self):            self.modified = True            self.accessed = True        super(SecureCookieSession, self).__init__(initial, on_update)        self.modified = False        self.accessed = False    def __getitem__(self, key):        self.accessed = True        return super(SecureCookieSession, self).__getitem__(key)    def get(self, key, default=None):        self.accessed = True        return super(SecureCookieSession, self).get(key, default)    def setdefault(self, key, default=None):        self.accessed = True        return super(SecureCookieSession, self).setdefault(key, default)

SecureCookieSession继承自werkzeug.datastructures模块的CallbackDict,CallbackDict实现了每次字典发生修改时,都会调用之前传入的函数的功能。这个功能的实现来自CallbackDict的父类UpdateDictMixin

class UpdateDictMixin(object):    """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类中所有可以修改数据的方法,都进行改写。首先定义了calls_update装饰器,这样像__setitem__实际是经过装饰后的方法,在装饰器中会找到同名的原方法调用,并且在self.on_update不为空的情况下,调用self.on_update。

 

回到flask中用到的SecureCookieSession,他定义了on_update方位为修改modified和accessed属性为真。这两个属相在后面保存session的时候会用到。

 

转载于:https://www.cnblogs.com/lovelaker007/p/8576880.html

你可能感兴趣的文章
Mac版OBS设置详解
查看>>
优雅地书写回调——Promise
查看>>
android主流开源库
查看>>
AX 2009 Grid控件下多选行
查看>>
PHP的配置
查看>>
Struts框架----进度1
查看>>
Round B APAC Test 2017
查看>>
MySQL 字符编码问题详细解释
查看>>
Ubuntu下面安装eclipse for c++
查看>>
让IE浏览器支持CSS3圆角属性的方法
查看>>
巡风源码阅读与分析---nascan.py
查看>>
LiveBinding应用 dataBind 数据绑定
查看>>
Linux重定向: > 和 &> 区别
查看>>
nginx修改内核参数
查看>>
C 筛选法找素数
查看>>
TCP为什么需要3次握手与4次挥手(转载)
查看>>
IOC容器
查看>>
Windows 2003全面优化
查看>>
URAL 1002 Phone Numbers(KMP+最短路orDP)
查看>>
web_day4_css_宽度
查看>>