zoukankan      html  css  js  c++  java
  • django session源码剖析

    首先要明白,session和cookie,session是保存在服务器端,cookie存储在浏览器上,我们称为客户端,客户端向服务端发送请求时,会将cookie一起发送给服务端。服务端接收到请求后,会去检查是否已经有该客户端的session信息,如果没有,则创建一个新的session对象,用于保存客户端的一些必要信息,如果从服务器上找到了该客户端的信息,则会将该信息加载到session里,

    django之所以能实现登陆认证,依靠的是一个叫sessionid的东西,该id记录了你的认证信息,如果你不喜欢这个名称,你也可以通过修改settings配置SESSION_COOKIE_NAME

    settings解说之session

    # Cache to store session data if using the cache session backend.
    SESSION_CACHE_ALIAS = 'default'  # 这个值对应CACHES里面的key
    # Cookie name. This can be whatever you want.
    SESSION_COOKIE_NAME = 'sessionid'
    # The module to store session data
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'
    # class to serialize session data
    SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer'
    
    #########
    # CACHE #
    #########
    
    # The cache backends to use.
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        }
    }
    

    . 在request请求模块里,默认是不带session功能的,也就是说request没有session属性的,为何我们还能使用呢?因为session功能是以中间件形式提供,由于在settings里配置了MIDDLEWARE_CLASSES这个变量,同时将模块'django.contrib.sessions.middleware.SessionMiddleware'添加到项目里,Django1.8以后的版本改名为MIDDLEWARE,所以我们才能通过request.session方式设置session。那么这个中间件对我们的request做了些什么呢?

        def process_request(self, request):
            session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)
            request.session = self.SessionStore(session_key)
    
    
    class WSGIHandler(base.BaseHandler):
        initLock = Lock()
        request_class = WSGIRequest
    
        def __call__(self, environ, start_response):
           ...
            try:
                request = self.request_class(environ)
                ...
    #通过上面这段代码,可以分析,request是django.core.handlers.wsgi.WSGIRequest()的实例
    #中间件首先从我们的请求request.COOKIES获取对应的cookie信息,这个信息最开始是从浏览器的cookie获取的,如果浏览器没有相应的cookie信息,则服务器会为这个浏览器创建一个cookie实例,本质上cookie对象就是一个继承了dict字典的对象,
    接着从cookie对象取出session_key,也就是cookie为 `sessionid `的值,,然后拿着这个值到self.SessionStore进行实例化,
    
    
    class SessionStore(SessionBase):
        """
        A cache-based session store.
        """
        def __init__(self, session_key=None):
            self._cache = caches[settings.SESSION_CACHE_ALIAS]  # 实例化时会通过settings配置加载一个用于存储缓存的媒介,该媒介用于存储session的,默认是媒介是引擎是db,即数据库,我们这里分析的是使用本地内存作为session缓存。caches会去导入settings配置的CACHES的backend存储引擎,并且做一些基本解析CACHES对应的key的参数配置,比如我们的`SESSION_CACHE_ALIAS`为default,那么会把default里的其他keys信息当成参数传到那个backend对象里进行实例化,最终赋值给session的self._cache变量
            super(SessionStore, self).__init__(session_key)  # 调用父类的构造方法,
    
        @property
        def cache_key(self):
            return KEY_PREFIX + self._get_or_create_session_key()  # 这个方法最终一定会获取到一个_session_key
    
        def load(self):  
            """
    这个方法调用是在假如需要对session进行修改操作时,比如设置session操作,那么会调用父类的__setitem__方法
        def __setitem__(self, key, value):
            self._session[key] = value
            self.modified = True
       def _get_session(self, no_load=False):
            """
            Lazily loads session from storage (unless "no_load" is True, when only
            an empty dict is stored) and stores it in the current instance.
            """
            self.accessed = True
            try:
                return self._session_cache
            except AttributeError:
                if self.session_key is None or no_load:
                    self._session_cache = {}
                else:
                    self._session_cache = self.load()
            return self._session_cache
        _session = property(_get_session)
    
    首先父类去获取_session属性,如果是第一次访问,肯定是没有缓存session的,也就是return self._session_cache这个对象暂时没有这个属性,第一次访问时,是没有session_key的,所以会 self._session_cache = {}创建一个空字典存储session缓存。第二次请求时,这时候可以从请求cookie里获取到 _session_key,那么,这时服务器可能已经记录过客户端的session信息了,为什么说是可能呢?因为如果选择使用本地内存方式存储,如果重启服务器,那么session将丢失,但是浏览器上已经写入了cookie信息,保留了_session_key.我们假如服务器没有重启过,那么服务器就保留了浏览器的session信息,这时会去加载这个session信息
    """
            try:
                session_data = self._cache.get(self.cache_key, None)  # 
            except Exception:
                # Some backends (e.g. memcache) raise an exception on invalid
                # cache keys. If this happens, reset the session. See #17810.
                session_data = None
            if session_data is not None:
                return session_data
            self._session_key = None
            return {}
    
        def create(self):
            # Because a cache can fail silently (e.g. memcache), we don't know if
            # we are failing to create a new session because of a key collision or
            # because the cache is missing. So we try for a (large) number of times
            # and then raise an exception. That's the risk you shoulder if using
            # cache backing.
            for i in range(10000):
                self._session_key = self._get_new_session_key()
                try:
                    self.save(must_create=True)
                except CreateError:
                    continue
                self.modified = True
                return
            raise RuntimeError(
                "Unable to create a new session key. "
                "It is likely that the cache is unavailable.")
    
        def save(self, must_create=False):
            if self.session_key is None:
                return self.create()
            if must_create:
                func = self._cache.add
            else:
                func = self._cache.set
            result = func(self.cache_key,
                          self._get_session(no_load=must_create),
                          self.get_expiry_age())
            if must_create and not result:
                raise CreateError
    
        def exists(self, session_key):
            return session_key and (KEY_PREFIX + session_key) in self._cache
    
        def delete(self, session_key=None):
            if session_key is None:
                if self.session_key is None:
                    return
                session_key = self.session_key
            self._cache.delete(KEY_PREFIX + session_key)
    
        @classmethod
        def clear_expired(cls):
            pass
    
  • 相关阅读:
    Effective C++ 读书笔记(3544):继承关系与面向对象设计
    《全景探秘游戏设计艺术》读后感之引子
    Effective C++ 读书笔记(1117):构造析构和赋值函数
    Effective C++ 读书笔记(2934):类与函数之实现
    Unity中使用PersistentDataPath加载文件
    打开本地【C】【D】【E】驱动器时候提示 X:\ 找不到应用程序
    C#进制转换
    在VS里编辑unity代码调用系统方法不显示中文注释或英文注释
    Spreadsheet说明
    C#中删除控件的事件的方法类.
  • 原文地址:https://www.cnblogs.com/zengchunyun/p/7055480.html
Copyright © 2011-2022 走看看