Flask source code interpretation 06: Session (session)

For the implementation of Session in Flask, readers can refer to other information about the principle of session and cookie. My understanding is as follows:

The server will create a sessionID for each new connection to identify each connection. In addition, the server can find the data saved before the connection on the server side according to the sessionID. This function has two advantages: saving data on the server side is more secure, and it is less likely to be attacked than the client side; the second is because the http protocol is a stateless protocol, that is, the latter connection will not carry the previous connection. any information. But if there is a session, subsequent connections can find the data stored in the server before being processed. So how is the sessionID of each connection saved, it is saved in the client's cookie. What is stored in the cookie is equivalent to a key, which can open the things stored in the server.

 

In flask, when the request environment is pushed into the stack, there is the following code

        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)            

If the session of the request environment is empty, a session object will be created based on the information in the request and the app. If the creation fails, an empty session will be created

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
        )

Looking at the open_session method in the above code, first create a serialized object s according to the app, and then check whether request.cookies.get(app.session_cookie_name) exists. This value is similar to the sessionID we mentioned earlier. If the value exists and pass s.loads If the method is successfully deserialized, it returns self.session_class(data) and returns the session instance initialized with data. If the value does not exist or the deserialization fails, return self.session_class() returns an uninitialized session instance.

Note that the method of storing user session data in flask is simplified here. The server mentioned above will store the user's session data, which is generally stored in a database. However, in flask, the data is serialized and stored directly in the cookie on the client side, but the serialized data can provide a certain degree of confidentiality.

 

The session class used by self.session_class(data) is 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 inherits from the CallbackDict of the werkzeug.datastructures module. CallbackDict implements the function of calling the function passed in each time the dictionary is modified. The implementation of this function comes from the parent class UpdateDictMixin of CallbackDict

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

As you can see, all methods in the UpdateDictMixin class that can modify data are rewritten. First, the calls_update decorator is defined, so that __setitem__ is actually a decorated method. The original method call with the same name will be found in the decorator, and self.on_update will be called when self.on_update is not empty.

 

Back to the SecureCookieSession used in flask, he defines the on_update orientation to modify the modified and accessed attributes to be true. These two zodiac signs will be used later when saving the session.

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324870196&siteId=291194637