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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM