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 # 返回响应

(完)

session源码剖析的更多相关文章

  1. django session源码剖析

    首先要明白,session和cookie,session是保存在服务器端,cookie存储在浏览器上,我们称为客户端,客户端向服务端发送请求时,会将cookie一起发送给服务端.服务端接收到请求后,会 ...

  2. SpringMVC源码剖析(二)- DispatcherServlet的前世今生

    上一篇文章<SpringMVC源码剖析(一)- 从抽象和接口说起>中,我介绍了一次典型的SpringMVC请求处理过程中,相继粉墨登场的各种核心类和接口.我刻意忽略了源码中的处理细节,只列 ...

  3. Appuim源码剖析(Bootstrap)

    Appuim源码剖析(Bootstrap) SkySeraph Jan. 26th 2017 Email:skyseraph00@163.com 更多精彩请直接访问SkySeraph个人站点:www. ...

  4. ThreadLocal终极源码剖析

    目录一.ThreadLocal1.1 源码注释1.2 源码剖析      散列算法-魔数0x61c88647      set操作    get操作    remove操作1.3 功能测试1.4 应用 ...

  5. 豌豆夹Redis解决方案Codis源码剖析:Proxy代理

    豌豆夹Redis解决方案Codis源码剖析:Proxy代理 1.预备知识 1.1 Codis Codis就不详细说了,摘抄一下GitHub上的一些项目描述: Codis is a proxy base ...

  6. ThreadLocal终极源码剖析-一篇足矣!

    本文较深入的分析了ThreadLocal和InheritableThreadLocal,从4个方向去分析:源码注释.源码剖析.功能测试.应用场景. 一.ThreadLocal 我们使用ThreadLo ...

  7. Flask核心机制--上下文源码剖析

    一.前言 了解过flask的python开发者想必都知道flask中核心机制莫过于上下文管理,当然学习flask如果不了解其中的处理流程,可能在很多问题上不能得到解决,当然我在写本篇文章之前也看到了很 ...

  8. Django----djagorest-framwork源码剖析

    restful(表者征状态转移,面向资源编程)------------------------------------------->约定 从资源的角度审视整个网络,将分布在网络中某个节点的资源 ...

  9. Rest_Framework之认证、权限、频率组件源码剖析

    一:使用RestFramwork,定义一个视图 from rest_framework.viewsets import ModelViewSet class BookView(ModelViewSet ...

随机推荐

  1. 2018-2019-2 20165325 《网络对抗技术》 Exp6 信息搜集与漏洞扫描

    2018-2019-2 20165325 <网络对抗技术> Exp6 信息搜集与漏洞扫描 实验内容(概要) 1 各种搜索技巧的应用: 2 DNS IP注册信息的查询: 3 基本的扫描技术 ...

  2. Shiro权限模型以及权限分配的两种方式

    1. 顶级账户分配权限用户需要被分配相应的权限才可访问相应的资源.权限是对于资源的操作一张许可证.给用户分配资源权限需要将权限的相关信息保存到数据库.这些相关内容包含:用户信息.权限管理.用户分配的权 ...

  3. JAVA学习笔记(1)—— eclipse自动补全和主题及字体配置

    1.自动补全功能 (1)打开 Eclipse -> Window -> Perferences (2)选择Java -> Editor -> Content Assist -& ...

  4. redis缓存雪崩、缓存穿透、数据库和redis数据一致性

    一.缓存雪崩 回顾一下我们为什么要用缓存(Redis):减轻数据库压力或尽可能少的访问数据库. 在前面学习我们都知道Redis不可能把所有的数据都缓存起来(内存昂贵且有限),所以Redis需要对数据设 ...

  5. 【codechef】FN/Fibonacci Number

    题意 给出 c 和 P ,求最小的非负整数 n 使得 \(Fib(n)=c(mod~ P)\) 其中 P 是质数且 模 10 等于一个完全平方数(也就是说 P 的末位是个完全平方数,那么只能是 1 或 ...

  6. 关于 git 本地创建 SSH Key 遇到的一点问题(①file to save the key & ②the authenticity of host...)

    背景 由于想测试一下 SSH Key 创建的路径(.ssh 目录路径)对于不同位置 git 项目是否有效. 比如,.ssh 默认在 C:\[users]\[username] 目录下,而项目 proj ...

  7. TypeError: can only concatenate str (not "int") to str解决方式

    使用format函数解决问题 for page in range(1,pagebox+1): url = "https://www.dd373.com/s/rbg22w-x9kjbs-wwf ...

  8. 微信小程序rich-text 文本首行缩进和图片居中

    微信小程序开发使用rich-text组件渲染html格式的代码,常常因为不能自定义css导致文本不能缩进,以及图片不能居中等问题,这里可以考虑使用js的replace方法,替换字符串,然后在渲染的同时 ...

  9. 学习笔记_J2EE_SpringMVC_02_注解配置

    SpringMVC注解配置 1.测试环境: 名称 版本 备注 操作系统 Windows10 专业版1809X64   WEB服务器 Tomcat 8.5 X64   浏览器 Google Chrome ...

  10. 想不想在mac上玩PSP?我教你呀

    OpenEmu for mac是一款针对OS X系统的原生开源游戏模拟器.有了它可以在Mac OS X 系统上玩GB.GBA.NDS.psP.PlayStation.超级任天堂(SNES).红白机(N ...