• Django session 源码流程


    流程

    Django session源码流程
    
    首先执行的是SessionMiddleware的init方法
        import_module(settings.SESSION_ENGINE) 导入了一个 django.contrib.sessions.backends.db的包
        将db中SessionStore 赋值给self.SessionStore 
    执行process_request    方法
        获取存放于cookie中的session_id,赋值为session_key
        实例化session仓库session_key作为参数,赋值给request.sessions
        
        执行SessionStore 的init方法
            执行父类SessionBase的init方法
                self._session_key = session_key
                将参数session_key 赋值给 self._session_key
                self.accessed = False  存取 session
                self.modified = False  修改session
            修改_session_key会执行下面
            _session_key = property(_get_session_key, _set_session_key)
            
            然后执行_set_session_key方法
                判断_session_key是否存在
                    T:将其赋值给__session_key
                    F:__session_key为None
        如果此时没有用户登录会执行__getitem__
            def __getitem__(self, key):
                return self._session[key]
                #self._session = _session_cache
        如果用户登录会给session赋值,执行__setitem__方法
            一键值对存放于self._session中
            self.modified = True 修改session
            
            因为self.session执行 _session = property(_get_session)
            执行_get_session方法
                self.accessed = True   #获取session
                
                如果self._session_cache 存在或者 no_load存在
                    self._session_cache = {}
                否则
                    self._session_cache = self.load()
                    
                return self._session_cache
    执行process_response方法
        accessed = request.session.accessed
        modified = request.session.modified
        判断session是否为空
            empty = request.session.is_empty()
                def is_empty(self):
                    "Returns True when there is no session_key and the session is empty"
                    try:
                        return not bool(self._session_key) and not self._session_cache    
                        return True
                如果此时self既有session_key又有session返回F,不为空(说明此时已经存在cookie与session)
                此时self如果没有属性_session_cache报错 为空
         判断sessionID是否存在于cookie中和 session是否存在(有cookie,没session)(绝对是注销操作)
            True,就删除并重新赋值cookie
         否则
            判断 accessed 存储是否为空(是否获取了session)
            判断session是否赋值和session不为空
                保存session并给cookie赋值
                    保存session request.session.save()
                        获取sessionID
                        获取那个字典
                        生成model实例
                        写入到数据库
                    给cookie赋值
                        session_id : fsufuhakjfhkuahfkdahkfhakfhakfhka
                        
                        
    1.一直处于未登录
        1.获取到cookie的session_id 为空
        2.获取session的某个值,为空
        3.session没有被赋值,不执行
    2.从未登录到登陆
        1.获取到cookie的session_id 为空
        2.给session赋值
        3.把session存放到数据库 sessionkey sessiondata 过期时间
        4.把sessionkey 存放到cookie sessionID 32位随机字符串
        
        认证原理:获取sessionkey 与数据库的sessionkey是否一致 一致就可取到 session data数据,然后取出判断此数据是否存在与数据库中
    3.一直登陆
        1.获取到cookie的session_id 为32位随机字符串
        2.获取session某值,不为空
        3.session没有被赋值,不执行
    4.注销
        1.获取到cookie的session_id 为32位随机字符串
        2.给session赋值
        3.把session存放到数据库 sessionkey sessiondata 过期时间 (sessiondata会被修改)
        4.把sessionkey 存放到cookie sessionID 32位随机字符串
        
        认证原理:获取cookie 的sessionkey 和 服务器的sessionkey字段 作比较 一致为 同一浏览器,但sessiondata 中的数据 向user表中查看
         
                        
                        
            
    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) #settings.SESSION_ENGINE = django.contrib.sessions.backends.db
            
            self.SessionStore = engine.SessionStore # class SessionStore(SessionBase): 实现数据库会话存储的类
    
        def process_request(self, request):
            session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)  #SESSION_COOKIE_NAME = 'sessionid'
            request.session = self.SessionStore(session_key)  #实例化 SessionStore
    
        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:   #cookie上的sessionkey sessiondata都为空 时执行这个
                    response.delete_cookie(
                        settings.SESSION_COOKIE_NAME,
                        path=settings.SESSION_COOKIE_PATH,  #SESSION_COOKIE_PATH = '/'
                        domain=settings.SESSION_COOKIE_DOMAIN,#SESSION_COOKIE_DOMAIN = None
                    )
                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
    SessionStore
    import logging
    
    from django.contrib.sessions.backends.base import (
        CreateError, SessionBase, UpdateError,
    )
    from django.core.exceptions import SuspiciousOperation
    from django.db import DatabaseError, IntegrityError, router, transaction
    from django.utils import timezone
    from django.utils.encoding import force_text
    from django.utils.functional import cached_property
    
    
    class SessionStore(SessionBase):
        """
        Implements database session store.
        """
        def __init__(self, session_key=None):
            super(SessionStore, self).__init__(session_key)
    
        @classmethod
        def get_model_class(cls):
            # Avoids a circular import and allows importing SessionStore when
            # django.contrib.sessions is not in INSTALLED_APPS.
            from django.contrib.sessions.models import Session
            return Session
    
        @cached_property
        def model(self):
            return self.get_model_class()
    
        def load(self):
            try:
                s = self.model.objects.get(
                    session_key=self.session_key,
                    expire_date__gt=timezone.now()
                )
                return self.decode(s.session_data)
            except (self.model.DoesNotExist, SuspiciousOperation) as e:
                if isinstance(e, SuspiciousOperation):
                    logger = logging.getLogger('django.security.%s' % e.__class__.__name__)
                    logger.warning(force_text(e))
                self._session_key = None
                return {}
    
        def exists(self, session_key):
            return self.model.objects.filter(session_key=session_key).exists()
    
        def create(self):
            while True:
                self._session_key = self._get_new_session_key()
                try:
                    # Save immediately to ensure we have a unique entry in the
                    # database.
                    self.save(must_create=True)
                except CreateError:
                    # Key wasn't unique. Try again.
                    continue
                self.modified = True
                return
    
        def create_model_instance(self, data):
            """
            Return a new instance of the session model object, which represents the
            current session state. Intended to be used for saving the session data
            to the database.
            """
            return self.model(
                session_key=self._get_or_create_session_key(),  #获取session_key
                session_data=self.encode(data),   #那个字典
                expire_date=self.get_expiry_date(),
            )
    
        def save(self, must_create=False):
            """
            Saves the current session data to the database. If 'must_create' is
            True, a database error will be raised if the saving operation doesn't
            create a *new* entry (as opposed to possibly updating an existing
            entry).
            """
            if self.session_key is None:
                return self.create()
            data = self._get_session(no_load=must_create) #此时已经存在值了
            obj = self.create_model_instance(data)   #model实例
            using = router.db_for_write(self.model, instance=obj)    #from django.contrib.sessions.models import Session
            try:
                with transaction.atomic(using=using):
                    #写入数据库
                    obj.save(force_insert=must_create, force_update=not must_create, using=using)
            except IntegrityError:
                if must_create:
                    raise CreateError
                raise
            except DatabaseError:
                if not must_create:
                    raise UpdateError
                raise
    
        def delete(self, session_key=None):
            if session_key is None:
                if self.session_key is None:
                    return
                session_key = self.session_key
            try:
                self.model.objects.get(session_key=session_key).delete()
            except self.model.DoesNotExist:
                pass
    
        @classmethod
        def clear_expired(cls):
            cls.get_model_class().objects.filter(expire_date__lt=timezone.now()).delete()

    session

    class SessionBase(object):
        """
        Base class for all Session classes.
        """
        TEST_COOKIE_NAME = 'testcookie'
        TEST_COOKIE_VALUE = 'worked'
    
        __not_given = object()
    
        def __init__(self, session_key=None):
            self._session_key = session_key
            self.accessed = False
            self.modified = False
            self.serializer = import_string(settings.SESSION_SERIALIZER)    #django.contrib.sessions.serializers.JSONSerializer
            #self.serializer = class JSONSerializer(object):  实现序列化方法的类
        def __contains__(self, key):
            return key in self._session
    
        def __getitem__(self, key):
            return self._session[key]
    
        def __setitem__(self, key, value):
            #self._session = _session_cache = {'name':'ldq'}
            self._session[key] = value  #因为字典是可变类型,所以当_session改变的时候_session_cache也随之改变
            self.modified = True
    
        def __delitem__(self, key):
            del self._session[key]
            self.modified = True
    
        def get(self, key, default=None):
            return self._session.get(key, default)
    
        def pop(self, key, default=__not_given):
            self.modified = self.modified or key in self._session
            args = () if default is self.__not_given else (default,)
            return self._session.pop(key, *args)
    
        def setdefault(self, key, value):
            if key in self._session:
                return self._session[key]
            else:
                self.modified = True
                self._session[key] = value
                return value
    
        def set_test_cookie(self):
            self[self.TEST_COOKIE_NAME] = self.TEST_COOKIE_VALUE
    
        def test_cookie_worked(self):
            return self.get(self.TEST_COOKIE_NAME) == self.TEST_COOKIE_VALUE
    
        def delete_test_cookie(self):
            del self[self.TEST_COOKIE_NAME]
    
        def _hash(self, value):
            key_salt = "django.contrib.sessions" + self.__class__.__name__
            return salted_hmac(key_salt, value).hexdigest()
    
        def encode(self, session_dict):
            "Returns the given session dictionary serialized and encoded as a string."
            serialized = self.serializer().dumps(session_dict)
            hash = self._hash(serialized)
            return base64.b64encode(hash.encode() + b":" + serialized).decode('ascii')
    
        def decode(self, session_data):
            encoded_data = base64.b64decode(force_bytes(session_data))
            try:
                # could produce ValueError if there is no ':'
                hash, serialized = encoded_data.split(b':', 1)
                expected_hash = self._hash(serialized)
                if not constant_time_compare(hash.decode(), expected_hash):
                    raise SuspiciousSession("Session data corrupted")
                else:
                    return self.serializer().loads(serialized)
            except Exception as e:
                # ValueError, SuspiciousOperation, unpickling exceptions. If any of
                # these happen, just return an empty dictionary (an empty session).
                if isinstance(e, SuspiciousOperation):
                    logger = logging.getLogger('django.security.%s' % e.__class__.__name__)
                    logger.warning(force_text(e))
                return {}
    
        def update(self, dict_):
            self._session.update(dict_)
            self.modified = True
    
        def has_key(self, key):
            return key in self._session
    
        def keys(self):
            return self._session.keys()
    
        def values(self):
            return self._session.values()
    
        def items(self):
            return self._session.items()
    
        def iterkeys(self):
            return self._session.iterkeys()
    
        def itervalues(self):
            return self._session.itervalues()
    
        def iteritems(self):
            return self._session.iteritems()
    
        def clear(self):
            # To avoid unnecessary persistent storage accesses, we set up the
            # internals directly (loading data wastes time, since we are going to
            # set it to an empty dict anyway).
            self._session_cache = {}
            self.accessed = True
            self.modified = True
    
        def is_empty(self):
            "Returns True when there is no session_key and the session is empty"
            try:
                return not bool(self._session_key) and not self._session_cache    #如果此时self既有session_key又有session返回F:不为空(说明此时已经存在cookie与session)
            except AttributeError:
                return True
    
        def _get_new_session_key(self):
            "Returns session key that isn't being used."
            while True:
                session_key = get_random_string(32, VALID_KEY_CHARS)  #32位的随机字符串
                if not self.exists(session_key):
                    break
            return session_key 
    
        def _get_or_create_session_key(self):
            if self._session_key is None:
                self._session_key = self._get_new_session_key()
            return self._session_key
    
        def _validate_session_key(self, key):
            """
            Key must be truthy and at least 8 characters long. 8 characters is an
            arbitrary lower bound for some minimal key security.
            """
            return key and len(key) >= 8
    
        def _get_session_key(self):
            return self.__session_key
    
        def _set_session_key(self, value):    #对象实例化时执行这个方法
            """
            Validate session key on assignment. Invalid values will set to None.
            """
            if self._validate_session_key(value):
                self.__session_key = value
            else:
                self.__session_key = None
    
        session_key = property(_get_session_key)  #None
        _session_key = property(_get_session_key, _set_session_key)  #self._session_key = self._get_new_session_key() #32位的随机字符串
    
        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   #正在获取session
            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)
    
        def get_expiry_age(self, **kwargs):
            """Get the number of seconds until the session expires.
    
            Optionally, this function accepts `modification` and `expiry` keyword
            arguments specifying the modification and expiry of the session.
            """
            try:
                modification = kwargs['modification']
            except KeyError:
                modification = timezone.now()
            # Make the difference between "expiry=None passed in kwargs" and
            # "expiry not passed in kwargs", in order to guarantee not to trigger
            # self.load() when expiry is provided.
            try:
                expiry = kwargs['expiry']
            except KeyError:
                expiry = self.get('_session_expiry')
    
            if not expiry:   # Checks both None and 0 cases
                return settings.SESSION_COOKIE_AGE
            if not isinstance(expiry, datetime):
                return expiry
            delta = expiry - modification
            return delta.days * 86400 + delta.seconds
    
        def get_expiry_date(self, **kwargs):
            """Get session the expiry date (as a datetime object).
    
            Optionally, this function accepts `modification` and `expiry` keyword
            arguments specifying the modification and expiry of the session.
            """
            try:
                modification = kwargs['modification']
            except KeyError:
                modification = timezone.now()
            # Same comment as in get_expiry_age
            try:
                expiry = kwargs['expiry']
            except KeyError:
                expiry = self.get('_session_expiry')
    
            if isinstance(expiry, datetime):
                return expiry
            if not expiry:   # Checks both None and 0 cases
                expiry = settings.SESSION_COOKIE_AGE    #SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2
    
            return modification + timedelta(seconds=expiry)
    
        def set_expiry(self, value):
            """
            Sets a custom expiration for the session. ``value`` can be an integer,
            a Python ``datetime`` or ``timedelta`` object or ``None``.
    
            If ``value`` is an integer, the session will expire after that many
            seconds of inactivity. If set to ``0`` then the session will expire on
            browser close.
    
            If ``value`` is a ``datetime`` or ``timedelta`` object, the session
            will expire at that specific future time.
    
            If ``value`` is ``None``, the session uses the global session expiry
            policy.
            """
            if value is None:
                # Remove any custom expiration for this session.
                try:
                    del self['_session_expiry']
                except KeyError:
                    pass
                return
            if isinstance(value, timedelta):
                value = timezone.now() + value
            self['_session_expiry'] = value
    
        def get_expire_at_browser_close(self):
            """
            Returns ``True`` if the session is set to expire when the browser
            closes, and ``False`` if there's an expiry date. Use
            ``get_expiry_date()`` or ``get_expiry_age()`` to find the actual expiry
            date/age, if there is one.
            """
            if self.get('_session_expiry') is None:
                return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
            return self.get('_session_expiry') == 0
    
        def flush(self):
            """
            Removes the current session data from the database and regenerates the
            key.
            """
            self.clear()
            self.delete()
            self._session_key = None
    
        def cycle_key(self):
            """
            Creates a new session key, while retaining the current session data.
            """
            data = self._session
            key = self.session_key
            self.create()
            self._session_cache = data
            if key:
                self.delete(key)
    
        # Methods that child classes must implement.
    
        def exists(self, session_key):
            """
            Returns True if the given session_key already exists.
            """
            raise NotImplementedError('subclasses of SessionBase must provide an exists() method')
    
        def create(self):
            """
            Creates a new session instance. Guaranteed to create a new object with
            a unique key and will have saved the result once (with empty data)
            before the method returns.
            """
            raise NotImplementedError('subclasses of SessionBase must provide a create() method')
    
        def save(self, must_create=False):
            """
            Saves the session data. If 'must_create' is True, a new session object
            is created (otherwise a CreateError exception is raised). Otherwise,
            save() only updates an existing object and does not create one
            (an UpdateError is raised).
            """
            raise NotImplementedError('subclasses of SessionBase must provide a save() method')
    
        def delete(self, session_key=None):
            """
            Deletes the session data under this key. If the key is None, the
            current session key value is used.
            """
            raise NotImplementedError('subclasses of SessionBase must provide a delete() method')
    
        def load(self):
            """
            Loads the session data and returns a dictionary.
            """
            raise NotImplementedError('subclasses of SessionBase must provide a load() method')
    
        @classmethod
        def clear_expired(cls):
            """
            Remove expired sessions from the session store.
    
            If this operation isn't possible on a given backend, it should raise
            NotImplementedError. If it isn't necessary, because the backend has
            a built-in expiration mechanism, it should be a no-op.
            """
            raise NotImplementedError('This backend does not support clear_expired().')
  • 相关阅读:
    Android开发学习——应用安装过程
    飞信接口
    sql联合查询
    宽度自适应
    数据绑定
    分页查询sql
    asp.net读取文件
    oracle数据库连接
    oracle服务的开始和关闭 CMD
    css导航条
  • 原文地址:https://www.cnblogs.com/ldq1996/p/8590435.html
Copyright © 2020-2023  润新知