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
    
  • 相关阅读:
    Oracle函数如何把符串装换为小写的格式
    Oralce中的synonym同义词
    JS中getYear()的兼容问题
    How to do SSH Tunneling (Port Forwarding)
    所谓深度链接(Deep linking)
    upload size of asp.net
    发一个自动刷网站PV流量的小工具
    解决Visual Studio 2008 下,打开.dbml(LINQ) 文件时,提示"The operation could not be completed." 的问题。
    在资源管理器中使鼠标右键增加一个命令,运行cmd,同时使得当前路径为资源管理器当前的目录
    使用SQL语句获取Sql Server数据库的版本
  • 原文地址:https://www.cnblogs.com/zengchunyun/p/7055480.html
Copyright © 2011-2022 走看看