zoukankan      html  css  js  c++  java
  • Django-session中间件源码简单分析

    Django-session中间件源码简单分析

    settings里有关中间件的配置

    复制代码
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'rbac.service.middleware.M1'
    ]
    
    from django.contrib.sessions.middleware import SessionMiddleware
    复制代码

    可以看到settings中都是字符串形式的,我们通过from django.contrib.sessions.middleware import SessionMiddleware导入,并查看SessionMiddleware类的内容

    SessionMiddleware

    复制代码
    import time
    from importlib import import_module
    
    from django.conf import settings
    from django.contrib.sessions.backends.base import UpdateError
    from django.core.exceptions import SuspiciousOperation
    from django.utils.cache import patch_vary_headers
    from django.utils.deprecation import MiddlewareMixin
    from django.utils.http import cookie_date
    
    
    class SessionMiddleware(MiddlewareMixin):
        def __init__(self, get_response=None):
            self.get_response = get_response
            engine = import_module(settings.SESSION_ENGINE)
            self.SessionStore = engine.SessionStore
    
        def process_request(self, request):
            session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
            request.session = self.SessionStore(session_key)
    
        def process_response(self, request, response):
            """
            If request.session was modified, or if the configuration is to save the
            session every time, save the changes and set a session cookie or delete
            the session cookie if the session has been emptied.
            """
            try:
                accessed = request.session.accessed
                modified = request.session.modified
                empty = request.session.is_empty()
            except AttributeError:
                pass
            else:
                # First check if we need to delete this cookie.
                # The session should be deleted only if the session is entirely empty
                if settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
                    response.delete_cookie(
                        settings.SESSION_COOKIE_NAME,
                        path=settings.SESSION_COOKIE_PATH,
                        domain=settings.SESSION_COOKIE_DOMAIN,
                    )
                else:
                    if accessed:
                        patch_vary_headers(response, ('Cookie',))
                    if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty:
                        if request.session.get_expire_at_browser_close():
                            max_age = None
                            expires = None
                        else:
                            max_age = request.session.get_expiry_age()
                            expires_time = time.time() + max_age
                            expires = cookie_date(expires_time)
                        # Save the session data and refresh the client cookie.
                        # Skip session save for 500 responses, refs #3881.
                        if response.status_code != 500:
                            try:
                                request.session.save()
                            except UpdateError:
                                raise SuspiciousOperation(
                                    "The request's session was deleted before the "
                                    "request completed. The user may have logged "
                                    "out in a concurrent request, for example."
                                )
                            response.set_cookie(
                                settings.SESSION_COOKIE_NAME,
                                request.session.session_key, max_age=max_age,
                                expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
                                path=settings.SESSION_COOKIE_PATH,
                                secure=settings.SESSION_COOKIE_SECURE or None,
                                httponly=settings.SESSION_COOKIE_HTTPONLY or None,
                            )
            return response
    复制代码

    初始化方法

    我们先看这个类的init初始化方法

    def __init__(self, get_response=None):
        self.get_response = get_response  
        engine = import_module(settings.SESSION_ENGINE)  
        self.SessionStore = engine.SessionStore

    首先定义self.get_response=get_response这里默认为None

    然后通过import_module导入模块,该方法可以通过字符串导入模块

    我们到settings中找到SESSION_ENGINE参数的值

    这里engine就等于上面导入的模块

    然后self.SessionStore = engine.SessionStore,我们可以看看engine模块中都有什么

    可以看到SessionStore就是一个类

    self.SessionStore就是这个类

    process_request

    def process_request(self, request):
        session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
        request.session = self.SessionStore(session_key)

    首先定义一个session_key

    这里的session_key其实就是COOKIE里带的以sessionid为key,随机字符串为值的值

    当我们第一次访问时其实session_key的值应该是None

    然后定义request.session,我们可以看到我们一直使用的request.session其实是self.SessionStore这个类实例化出的对象

    实例化的过程执行了什么呢

    SessionStore类初始化时调用了其父类的__init__方法

    父类的__init__方法首先定义了self._session_key=session_key,第一次访问和默认情况都是None

    然后定义了两个变量self.accessed和self.modified都是False

    最后的self.serializer是序列化时的方法,我们暂时不看

    到此process_request就执行完了

    视图函数

     在执行视图函数时我们可能会对request.session进行操作,例如登录成功后我们要做以下设置

    request.session["user_id"] = user.pk

    我们在学习面向对象的知识时曾经学过这种类似于操作字典一样的方法,在对象的父类中需要有__getitem__,__setitem__,__delitem__方法,才能这么操作,而这么操作了也就相当于执行了前面的几个方法

    在SessionStore的父类SessionBase中我们找到了这几个方法

    我们这里的操作其实就是执行的下面的代码

    def __setitem__(self, key, value):
        self._session[key] = value
        self.modified = True

    首先self._session[key] = value,这个self._session是个什么呢

    继续在SessionBase类中查找

    从上面的代码可以看出self._session其实就是self._session_cache,如果第一次访问,self._session_cache不存在,所以会抛出异常,走except的内容,给self._session_cache赋一个空字典,这里给这个字典添加了我们设置的键值对

    然后将self.accessed和self.modified的值变为True

    当视图函数执行完成后,我们又要走中间件的process_response方法

    process_response

    复制代码
    def process_response(self, request, response):
        """
        If request.session was modified, or if the configuration is to save the
        session every time, save the changes and set a session cookie or delete
        the session cookie if the session has been emptied.
        """
        try:
            accessed = request.session.accessed
            modified = request.session.modified
            empty = request.session.is_empty()
        except AttributeError:
            pass
        else:
            # First check if we need to delete this cookie.
            # The session should be deleted only if the session is entirely empty
            if settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
                response.delete_cookie(
                    settings.SESSION_COOKIE_NAME,
                    path=settings.SESSION_COOKIE_PATH,
                    domain=settings.SESSION_COOKIE_DOMAIN,
                )
            else:
                if accessed:
                    patch_vary_headers(response, ('Cookie',))
                if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty:
                    if request.session.get_expire_at_browser_close():
                        max_age = None
                        expires = None
                    else:
                        max_age = request.session.get_expiry_age()
                        expires_time = time.time() + max_age
                        expires = cookie_date(expires_time)
                    # Save the session data and refresh the client cookie.
                    # Skip session save for 500 responses, refs #3881.
                    if response.status_code != 500:
                        try:
                            request.session.save()
                        except UpdateError:
                            raise SuspiciousOperation(
                                "The request's session was deleted before the "
                                "request completed. The user may have logged "
                                "out in a concurrent request, for example."
                            )
                        response.set_cookie(
                            settings.SESSION_COOKIE_NAME,
                            request.session.session_key, max_age=max_age,
                            expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
                            path=settings.SESSION_COOKIE_PATH,
                            secure=settings.SESSION_COOKIE_SECURE or None,
                            httponly=settings.SESSION_COOKIE_HTTPONLY or None,
                        )
        return response
    复制代码

    首先定义了三个变量

    如果执行了上面视图函数中的操作,那么accessed和modified应该都是True

    is_empty()执行的代码如下

    从前面的代码可以看到我们的self._session其实就是_session_cache,而我们给他做了赋值,所有这里的empty是False

    接着往下走

    前面的赋值操作未出现异常我们执行else后的if判断,由于empty为False,所以执行else后的代码

    这里可以看到其实就是定义了session的过期时间等相关的属性

    接着,当后台运行没错,状态码不是500时,我们执行request.session.save(),这里save具体做了什么呢

    首先判断self.session_key是否为None,如果是第一次访问则为None,执行self.create()

    这个方法里首先用self._get_new_session_key()方法生成了一个随机字符串赋值给self._session_key,然后继续执行self.save方法,并且设置must_create=True

    这时再进入save方法,设置data = self._get_session(no_load=must_create),self._get_session方法执的结果其实就是上面的self._session_cache,就是我们设置的有键值对的字典

    所以data的值就是我们设置的保存用户信息的字典

    然后执行obj = self.create_model_instance(data),self.create_model_instance(data)执行下面的代码

    其实就是返回了一个包含session_key,session_data等字段的model对象

    然后调用事务,将数据保存到数据库

    这里第一次访问时,must_create是True,所以是添加操作,而当后面再访问时,must_create是False,所以执行的是更新操作

    数据保存完成后,我们再用response.set_cookie设置了COOKIE值,最后将结果返回

  • 相关阅读:
    PHP:_SERVER变量和_ENV变量列表
    一些使用jQuery開發的在線Web富文本編輯器
    xhtml+css排版,清理浮动整理汇总
    PHP 6 的新特性
    用PHP的CURL写的一个采集Discuz的例子
    SA、SD、SE 这三者的含义及区分
    PHP 之父 Rasmus Lerdorf 谈PHP开发
    http头的组成
    Jquery插件 可以随着滚动条的滚动而即时加载图片
    在你的网页中嵌入PDF等文件,实现pdf在线阅读
  • 原文地址:https://www.cnblogs.com/QQ279366/p/8525362.html
Copyright © 2011-2022 走看看