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

    session机制采用的是一种在客户端与服务端之间保持状态的解决方案,由于采用服务器端保持状态的方案在客户端也要保存标识,session机制也要借助于cookie机制达到目的。session保存了客户的登录信息,但是不需要把用户的所有信息都保存在session中,我们只需要让与用户数据关联的信息保存在session中就可以了。

    request.session[“user_id”] = 2  # 设置session  关联id比关联username好,因为username可能不唯一
    request.session.get["user_id"]  # 取session

    接下来看源码:

    from django.contrib.sessions.middleware import 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 # 这是个空变量,get_response=None engine = import_module(settings.SESSION_ENGINE) # 用了两个settings self.SessionStore = engine.SessionStore def process_request(self, request):...
    # 下面一个方法是views视图要走的
    def process_response(self, request, response):...

    先看这个settings,点击

    from django.conf import global_settings  # 这里面有global_settings,点击看源码
    
    ......
    
    settings = LazySettings()
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'  # 这个文件里面会看到SESSION_ENGINE

    这里面可与看到SESSION_ENGINE是一条配置信息,这里默认将session的信息放到djano数据库里面的session表里面。所以我们可以通过这里面的配置,将session信息放到缓存、文件、Memcache中。另外可以从from django.conf import settings看出,我们这里引用的settings不仅仅是我们django的settings.py文件还有global_settings.py文件,一共两套,先找我们常用的settings.py如果找不到,再去global_settings.py文件里面找。

    另外一点值得注意的是:

    engine = import_module(settings.SESSION_ENGINE)  
    # SESSION_ENGINE = 'django.contrib.sessions.backends.db' 是一个字符串
    # import_module("django.contrib.sessions.backends.db") 和 importdjango.contrib.sessions.backends.db 效果是一样的
    # 引入模块之后,再赋值给 engine,所以优先用自己写的engine

    就接下来看:

    class SessionMiddleware(MiddlewareMixin):
        def __init__(self, get_response=None):
            self.get_response = get_response  # 这是个空变量,get_response=None
            engine = import_module(settings.SESSION_ENGINE)  # 用了两个settings
            self.SessionStore = engine.SessionStore  # 这里可以看到,因为模块名是engine,所以SessionStore一定是它里面的变量名

    继续去看源码:

    from django.contrib.sessions.backends import db  # 点击db看源码

    可以看到,SessionStore是一个类的名字,self.SessionStore这里就是一个类名,所有的功能都封装在SessionStore里面,非常重要。

    class SessionStore(SessionBase):...

    走到这里,__init__这个函数就执行完了。继续执行process_request方法。

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

    先去常用的settings.py文件里找,这里找不到,再去global_settings.py里面找SESSION_COOKIE_NAME,就会看到:

    SESSION_COOKIE_NAME = 'sessionid'  # 这里拿到了sessionid
    
    
    session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)# 拿到sessin后进行了一个取的操作,没有sessionid键值对,session_key就是None

    所以用户登陆进来,一定会去取这个cookie,无论是第一次还是第二次登陆,不管cookie是不是空的,都要去取cookie里面的sessionid这个键,如果是第一次登陆肯定没有sessionid这个键,如果是第二次第三次登陆肯定有我给你的这个键。

    request.session = self.SessionStore(session_key)
    # SessionStore是一个功能类,里面传一个参数,就是实例化一个对象,所以request.session就是一个对象。

    继续去db里面看SessionStore做了什么。

    class SessionStore(SessionBase):
    
        def __init__(self, session_key=None):  # session_key默认为空
            super(SessionStore, self).__init__(session_key)

    将session_key赋予了__init__方法,继承了父类方法,如果还要执行父类的__init__方法就用super()方法。

    进入SessionBase这个父类看__init__方法。

    class SessionBase(object):
    
        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)  # 序列化,用到JSONSerializer进行序列化的,去global_settings找得到

    到这一步request_session方法也走完了,其实就是得到一个request.session对象。

    这样就通过中间件,就该去执行视图了。

    __init__方法和process_request()方法属于走中间件的内容。

    视图流程views:

    request.session["user_id"] = 1 # 这是一个赋值操作

    继续去SessionBase里面看源码:

    class SessionBase(object):  # 里面还有下面三个方法
    
        def __getitem__(self, key):  # 这个是获取值操作,但是必须是print(a['k'])这种操作才能出发这个方法
            return self._session[key]
    
        def __setitem__(self, key, value):  # 这个是设置值操作,但是赋值操作必须是a['k'] = v 这种形式才会触发这个方法
            self._session[key] = value
            self.modified = True
    
        def __delitem__(self, key):  # 这个是删除值操作
            del self._session[key]
            self.modified = True

     然后看这个_session的功能。SessionBase里面的方法里面有_session,那么它还有类方法:

    _session = property(_get_session)  # 会找到这个,后面加property就是去执行_get_session方法去了
        def _get_session(self, no_load=False):
    
            self.accessed = True  # 这个前面默认False,通过这个状态就可以区分登陆没有
            try:
                return self._session_cache  # 返回的就是session的缓存,和db有关,就可以认为缓存里带着db里面的数据,这里就是数据真正保存的地方
            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)

    赋值操作:

    request.session["user_id"] = 1 # 这个操作结束后,并没有存入数据库,而是在缓存中,因为没有执行save()操作。

    继续走到相应的操作,但是还没返回到用户的客户端。

        def process_response(self, request, response):
            try:
                accessed = request.session.accessed  # 先取出这两个变量,登陆成功执行了request.session["user_id'] = 1 他们都是True了
                modified = request.session.modified  # 如果没有执行这个赋值操作,他们依然是False
                empty = request.session.is_empty()  # 点击is_empty()看源码
      
        def is_empty(self):
            try:
    # not boo(self._session_key) 这里是True 因为这时第一次登陆_session_key时None
    # not self._session_cache 是 False 因为这是缓存里有值
    # True and False 结果就是 False 了,就是返回False
    return not bool(self._session_key) and not self._session_cache except AttributeError: return True

    所以empty就是None了 

    def process_response(self, request, response):
    try:
            accessed = request.session.accessed  # accessed变量,登陆成功并执行了request.session['user_id'] = 1 状态就变成True
    modified = request.session.modified # modified变量,登陆成功并自行了request.session['user_id'] = 1 状态就变成Trye
    empty = request.session.is_empty()
    except AttributeError:
    pass
    else: # 下面就是检验就没有进行request.session["user_id"] = 2 这个赋值操作
    if settings.SESSION_COOKIE_NAME in request.COOKIES and empty: # empty是False,所以这句话就不执行了
    response.delete_cookie(
    settings.SESSION_COOKIE_NAME,
    path=settings.SESSION_COOKIE_PATH,
    domain=settings.SESSION_COOKIE_DOMAIN,
    )
    else: # 就走这句话
    if accessed: # 刚才accessed是True 所以这句话执行
    patch_vary_headers(response, ('Cookie',))
    # not empty 是True
    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)
    # 到这结束 和过期时间相关
    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(
    # 这里是session_id 的key
    settings.SESSION_COOKIE_NAME,
    # 这里是session_id的value,这里就拿到了随机字符串
    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 # 返回响应

     (完)

  • 相关阅读:
    202011.19
    202011.18
    202011.17
    202011.16
    202011.14
    jdk的下载和配置
    layui中form表单
    JS中utocomplete
    转:JqueryUI学习笔记-自动完成autocomplete
    JSON.parse()与JSON.stringify()的区别
  • 原文地址:https://www.cnblogs.com/aaronthon/p/9467402.html
Copyright © 2011-2022 走看看