GVKun编程网logo

cookie,session,django中间件,csrf回顾(scrapy cookie中间件)

166

在本文中,您将会了解到关于cookie,session,django中间件,csrf回顾的新资讯,同时我们还将为您解释scrapycookie中间件的相关在本文中,我们将带你探索cookie,sess

在本文中,您将会了解到关于cookie,session,django中间件,csrf回顾的新资讯,同时我们还将为您解释scrapy cookie中间件的相关在本文中,我们将带你探索cookie,session,django中间件,csrf回顾的奥秘,分析scrapy cookie中间件的特点,并给出一些关于5 - django-csrf-session&cookie、cookie与session,Django中间件,跨站请求伪造、cookie和session django中间件、cookie操作, session操作, django中间件的实用技巧。

本文目录一览:

cookie,session,django中间件,csrf回顾(scrapy cookie中间件)

cookie,session,django中间件,csrf回顾(scrapy cookie中间件)

cookie与session

cookie与session的概念

​ 1.起源 ​ 由于http协议是无状态的无法保存用户状态 所以需要发明一些能够保存用户状态的技术 ​ 2.cookie ​ 保存在浏览器上的键值对 ​ cookie虽然是保存在客户端浏览器上的,但是是服务端设置的 ​ 也就意味着 浏览器有权限禁止服务端写入cookie ​ 3.session ​ 由于cookie是将所有的关键性信息保存在客户端浏览器上的 数据不是很安全 ​ 所以有了session:session就是将数据保存在服务端 只给客户端浏览器一个随机字符串 ​ 服务端记录了 随机字符串与真实数据的对应关系

django如何操作cookie与session

1 django操作cookie
由于视图函数返回的都是HttpResponse对象 所以可以利用该对象来操作cookie
return HttpResponse()

obj = HttpResponse()
obj.set_cookie(key,value)  # 告诉浏览器在本地保存一个键值对
obj.set_cookie(key,value,max_age=5)  # 还可以设置超时时间
return obj 

		
2.基于cookie做用户的登录认证
	request.COOKIES.get(key)  # 检验用户是否登录

    如何保存用户在没有登录之前想访问的那个页面的url,然后当用户用户名和密码输入正确点击登录之后再跳转会去
    request.path_info  # 只拿后缀
    request.get_full_path()  # 后缀加get请求携带的参数

    利用上面的方法在装饰器中获取用户想要访问的url
    在跳转到登录页面的url的时候 以get请求携带参数的方式将用户想要访问的url携带过去
    在登录的视图函数中 对url进行判断 
    	1.用户没有登录的情况下访问了一个必须登录的页面
    	2.用户直接访问的就是登录页面

3.操作session
	设置
		request.session[''key''] = value
		"""
		1.django内部自动随机生成一个随机字符串
		2.在服务端默认情况下需要django_session表来存储session信息(没有执行数据库迁移命令会报错)
			django session默认的过期时间是14天
		3.将随机字符串发送给客户端浏览器 让浏览器写入cookie
			sessionid : 随机字符串
		"""
	获取
		request.session.get(''key'')
		"""
		1.会自动去客户端浏览器发送过来的数据中查找cookie信息
		2.拿着该信息去django_session表中比对
		3.无论有没有数据 你都可以通过request.session.get()
			1.没有返回的结果就是None
			2.有则返回随机字符串所对应的真实数据               
		"""
	设置超时时间
		request.session.set_expiry(value)
			value 
				数字
				0
				时间格式
				不写
	
	删除session  
		request.session.flush()
		
		request.session.delete()
	
	额外知识点补充
		session是保存在服务端上的键值对
			1.数据库
			2.文件
			3.缓存数据库
			4....
		一句话 后端可以用很多种方式来保存session信息 并不是只能保存在数据库中      

django中间件

django中间件是django的门户 任何的请求来和响应走都需要经过中间件,所以中间件在做一些网站全局性的功能时特别好用 1.校验用户访问频率 2.校验用户黑名单 白名单 3.获取用户权限 在web领域 权限其实就是一个个的url (CRM项目) RBAC 基于角色权限管理 权限表

​ 用户表

​ 角色表

用户表和角色表 可以是多对多

角色和权限也是一个 多对多

1 实现给每一个角色绑定响应的权限 2 创建用户的时候只需要指定该用户的角色即可 3 在用户登录的时候 在中间件中获取到该用户所有的权限 4 然后用户每一次访问url的时候 都去校验是都在用户可以访问的url列表中

django默认有七个中间件 并且支持用户自定义自己的中间件

1 django暴露给用户五个可以自定义的方法
	需要掌握的
		process_request
			1.请求来的时候 会按照settings配置文件中从上往下的顺序依次执行每一个中间件里面的该方法 
			2.中间件里面如果没有定义该方法 直接跳过执行下一个
			3.该方法内如果你自己返回了HttpResponse对象 那么不再往后执行  直接跳到同级别的process_response方法
		process_response
			1.响应走的时候 会按照settings配置文件中从下往上的顺序依次执行每一个中间件里面的该方法
			2.该方法必须将形参response返回
			3.如果没有定义该方法 直接跳过执行下一个
	
需要了解的
	process_view
		当路由匹配成功之后执行视图函数之前
	
	process_exception
		当视图函数中出现bug的时候 自动触发
	
	process_template_response
		返回的对象中 必须含有render方法 才会触发

2.如何自定义

1 新建一个专门用来存放中间件的文件夹 里面新建任何名称的py文件
在该文件中 需要写类并继承MiddleMixin
class MyMiddle(MiddleMixin):
	# 自定义上面的五个方法

2 需要在setting配置文件中注册你写的中间件
	需要你自己手动书写字符串的路径  写到类名为止

csrf跨站请求伪造

钓鱼网站
正规网站在返回给用户含有post请求的页面 都附带了一个随机的字符串
然后下一次用户在提交post请求的时候 会先校验该随机字符串是否存在并判断是否一致

form表单
	在form标签内写
		{% csrf_token %}
	
ajax中如何避免
	
	1.通过标签查找的方式
		{''csrfmiddlewaretoken'':$(''[name="csrfmiddlewaretoken"]'').val()}
	2.第二种 
		{''csrfmiddlewaretoken'':''{{ csrf_token }}''}
	3.第三种
		# 直接拷贝官网提供的js文件 动态获取
		# 你只需要在你的页面上导入该文件即可
	   

相关装饰器
	csrf_exempt
	
	csrf_protect
	
	在给CBV装饰的时候有区别
		1.需要导入一个固定的装饰器 method_decorator(装饰器,方法名)
		
	
		csrf_exempt这个装饰器比较特殊 装饰的时候 只能给dispatch方法装
		
		
		2.其他的装饰器  自定义的 模块的
			1.直接类外面指名道姓的装
			2.给dispatch装
			3.直接装在方法上

5 - django-csrf-session&cookie

5 - django-csrf-session&cookie

[toc]

1 CSRF 跨站请求伪造

        CSRF 跨站点请求伪造 (Cross—Site Request Forgery),跟 XSS 攻击一样,存在巨大的危害性,你可以这样来理解:攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。

1.1 CSRF 攻击介绍及防御

人设:Web A 为存在 CSRF 漏洞的网站,Web B 为攻击者构建的恶意网站,User C 为 Web A 网站的合法用户。
CSRF 攻击攻击原理及过程如下:

  1. 用户 C 打开浏览器,访问受信任网站 A,输入用户名和密码请求登录网站 A;
  2. 在用户信息通过验证后,网站 A 产生 Cookie 信息并返回给浏览器,此时用户登录网站 A 成功,可以正常发送请求到网站 A;
  3. 用户未退出网站 A 之前,在同一浏览器中,打开一个 TAB 页访问网站 B;
  4. 网站 B 接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点 A;
  5. 浏览器在接收到这些攻击性代码后,根据网站 B 的请求,在用户不知情的情况下携带 Cookie 信息,向网站 A 发出请求。网站 A 并不知道该请求其实是由 B 发起的,所以会根据用户 C 的 Cookie 信息以 C 的权限处理该请求,导致来自网站 B 的恶意代码被执行。

PS:还有一种情况是:多窗口浏览器,它便捷的同时也带来了一些问题,因为多窗口浏览器新开的窗口是具有当前所有会话的。即我用 IE 登陆了我的 Blog,然后我想看新闻了,又运行一个 IE 进程,这个时候两个 IE 窗口的会话是彼此独立的,从看新闻的 IE 发送请求到 Blog 不会有我登录的 cookie;但是多窗口浏览器永远都只有一个进程,各窗口的会话是通用的,即看新闻的窗口发请求到 Blog 是会带上我在 blog 登录的 cookie,也有可能产生 CSRF 攻击。

下面是一个实例:

1. 受害者 Bob 在银行有一笔存款,通过对银行的网站发送请求 http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 可以使 Bob  1000000 的存款转到 bob2 的账号下。通常情况下,该请求发送到网站后,服务器会先验证该请求是否来自一个合法的 session,并且该 session 的用户 Bob 已经成功登陆。

2. 黑客 Mallory 自己在该银行也有账户,他知道上文中的 URL 可以把钱进行转帐操作。Mallory 可以自己发送一个请求给银行:http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory。但是这个请求来自 Mallory 而非 Bob,他不能通过安全认证,因此该请求不会起作用。

3. 这时,Mallory 想到使用 CSRF 的攻击方式,他先自己做一个网站,在网站中放入如下代码: src=”http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory ”,并且通过广告等诱使 Bob 来访问他的网站。当 Bob 访问该网站时,上述 url 就会从 Bob 的浏览器发向银行,而这个请求会附带 Bob 浏览器中的 cookie 一起发向银行服务器。大多数情况下,该请求会失败,因为他要求 Bob 的认证信息。但是,如果 Bob 当时恰巧刚访问他的银行后不久,他的浏览器与银行网站之间的 session 尚未过期,浏览器的 cookie 之中含有 Bob 的认证信息。这时,悲剧发生了,这个 url 请求就会得到响应,钱将从 Bob 的账号转移到 Mallory 的账号,而 Bob 当时毫不知情。等以后 Bob 发现账户钱少了,即使他去银行查询日志,他也只能发现确实有一个来自于他本人的合法请求转移了资金,没有任何被攻击的痕迹。而 Mallory 则可以拿到钱后逍遥法外。 

1.2 防御 CSRF 攻击

        目前防御 CSRF 攻击主要有三种策略:验证 HTTP Referer 字段;在请求地址中添加 token 并验证;在 HTTP 头中自定义属性并验证。

1.2.1 验证 HTTP Referer 字段

        根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。在通常情况下,访问一个安全受限页面的请求来自于同一个网站,比如需要访问 http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory,用户必须先登陆 bank.example,然后通过点击页面上的按钮来触发转账事件。这时,该转帐请求的 Referer 值就会是转账按钮所在的页面的 URL,通常是以 bank.example 域名开头的地址。而如果黑客要对银行网站实施 CSRF 攻击,他只能在他自己的网站构造请求,当用户通过黑客的网站发送请求到银行时,该请求的 Referer 是指向黑客自己的网站。因此,要防御 CSRF 攻击,银行网站只需要对于每一个转账请求验证其 Referer 值,如果是以 bank.example 开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果 Referer 是其他网站的话,则有可能是黑客的 CSRF 攻击,拒绝该请求。

        这种方法的显而易见的好处就是简单易行,网站的普通开发人员不需要操心 CSRF 的漏洞,只需要在最后给所有安全敏感的请求统一增加一个拦截器来检查 Referer 的值就可以。特别是对于当前现有的系统,不需要改变当前系统的任何已有代码和逻辑,没有风险,非常便捷。

        然而,这种方法并非万无一失。Referer 的值是由浏览器提供的,虽然 HTTP 协议上有明确的要求,但是每个浏览器对于 Referer 的具体实现可能有差别,并不能保证浏览器自身没有安全漏洞。使用验证 Referer 值的方法,就是把安全性都依赖于第三方(即浏览器)来保障,从理论上来讲,这样并不安全。事实上,对于某些浏览器,比如 IE6 或 FF2,目前已经有一些方法可以篡改 Referer 值。如果 bank.example 网站支持 IE6 浏览器,黑客完全可以把用户浏览器的 Referer 值设为以 bank.example 域名开头的地址,这样就可以通过验证,从而进行 CSRF 攻击。

        即便是使用最新的浏览器,黑客无法篡改 Referer 值,这种方法仍然有问题。因为 Referer 值会记录下用户的访问来源,有些用户认为这样会侵犯到他们自己的隐私权,特别是有些组织担心 Referer 值会把组织内网中的某些信息泄露到外网中。因此,用户自己可以设置浏览器使其在发送请求时不再提供 Referer。当他们正常访问银行网站时,网站会因为请求没有 Referer 值而认为是 CSRF 攻击,拒绝合法用户的访问。

1.2.2 在请求地址中添加 token 并验证

        CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。

        这种方法要比检查 Referer 要安全一些,token 可以在用户登陆后产生并放于 session 之中,然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。 而对于 POST 请求来说,要在 form 的最后加上 <input type=”hidden” name=”csrftoken” value=”tokenvalue”/>,这样就把 token 以参数的形式加入请求了。但是,在一个网站中,可以接受请求的地方非常多,要对于每一个请求都加上 token 是很麻烦的,并且很容易漏掉,通常使用的方法就是在每次页面加载时,使用 javascript 遍历整个 dom 树,对于 dom 中所有的 a 和 form 标签后加入 token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的 html 代码,这种方法就没有作用,还需要程序员在编码时手动添加 token。

        该方法还有一个缺点是难以保证 token 本身的安全。特别是在一些论坛之类支持用户自己发表内容的网站,黑客可以在上面发布自己个人网站的地址。由于系统也会在这个地址后面加上 token,黑客可以在自己的网站上得到这个 token,并马上就可以发动 CSRF 攻击。为了避免这一点,系统可以在添加 token 的时候增加一个判断,如果这个链接是链到自己本站的,就在后面添加 token,如果是通向外网则不加。不过,即使这个 csrftoken 不以参数的形式附加在请求之中,黑客的网站也同样可以通过 Referer 来得到这个 token 值以发动 CSRF 攻击。这也是一些用户喜欢手动关闭浏览器 Referer 功能的原因。

1.2.3 在 HTTP 头中自定义属性并验证

        这种方法也是使用 token 并进行验证,和上一种方法不同的是,这里并不是把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。         然而这种方法的局限性非常大。XMLHttpRequest 请求通常用于 Ajax 方法中对于页面局部的异步刷新,并非所有的请求都适合用这个类来发起,而且通过该类请求得到的页面不能被浏览器所记录下,从而进行前进,后退,刷新,收藏等操作,给用户带来不便。另外,对于没有进行 CSRF 防护的遗留系统来说,要采用这种方法来进行防护,要把所有请求都改为 XMLHttpRequest 请求,这样几乎是要重写整个网站,这代价无疑是不能接受的。

1.2.4 django csrf token

        django 中,是通过中间件来对 CSRF 进行验证的,有关中间件的信息,将在后面的小节中说明。PS:还记得之前在 settings.py 中注释掉的 django.middleware.csrf.CsrfViewMiddleware 吗?,它就是用于检测 CSRF 的中间件。如果我们 render 返回页面时,那么用户使用 get 请求访问网站时,站点会返回一段随机的 csrf_token,同时也会在 cookie 中写一个名为 csrftoken 的 key,这两个 key 对应的值是不同的。

        根据这两种 token,我们有两种方式,传递、验证 CSRF token(首先打开 csrf 中间件,如果之前注释掉的话)即:1、form 表单提交 2、ajax 提交

1.3 form 表单提交

        在表单中添加 {% csrf_token %} 来渲染服务端返回的 csrf_token,它会以 hidden 的形式在 form 中产生一个 input 标签,在我们利用 form 提交时会一并携带发送给服务端进行验证,当我们使用 ajax 代替 form 的 submit 进行提交时,可以在 data 字段,来获取 form 表单中渲染的 csrf_token,进行提交,当然,这里的 key 的名称必须为 csrfmiddlewaretoken,否则无法识别,从生成的 input 的 name 属性就能看到。

<form action="/login" method="post" >
{% csrf_token %}        #渲染csrf_token
    <div>
        <label for="user">用户名:</label>
        <input type="text" id="user" name="user" placeholder=''Username''>
    </div>
    <div>
        <label for="passwd">密码:</label>
        <input type="password" id="passwd" name="passwd" placeholder=''Password''>
    </div>
        <input type="submit" value="登陆">
</form>

在前端我们看到的是:

<form action="/file" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrfmiddlewaretoken" value="8HElv24C9ajJZUE8gX3Cs9e0iIaXgX9iMvIfXZsKthjazqwnAvtrEvS84QC2pEik">   # 渲染的input标签
    <div>
        <label for="user">用户名:</label>
        <input type="text" id="user" name="user" placeholder=''Username''>
    </div>
    <div>
        <label for="passwd">密码:</label>
        <input type="password" id="passwd" name="passwd" placeholder=''Password''>
    </div>
        <input type="submit" value="登陆">
</form>

通过 AJAX 来获取 Form 的 csrftoken 进行提交 

$(function(){
    $(''#btn'').click(function () {
        $.ajax({
            url:''/login'',
            type:''POST'',
            data:{''csrfmiddlewaretoken'':$("input[name=''csrfmiddlewaretoken'']").val(),......},    // name属性选择器获取csrftoken
            success:function () {
                alert(123)
        }
        })
    })
})

为什么是 ''csrfmiddlewaretoken'' 这个名字?

# 1、倒入 csrf中间件查看源码,获取的到底是什么信息
from django.middleware.csrf import CsrfViewMiddleware
 
# 2、源码中关于from的csrf定义 295行
request_csrf_token = ""
if request.method == "POST":
    try:
        request_csrf_token = request.POST.get(''csrfmiddlewaretoken'', '''')   # 这里获取了csrfmiddlewaretoken,所以我们form必须要传递这个key才行。

1.4 ajax 提交  

        当页面中没有 form 表单 (或者说不想使用 form 手动渲染),那么还可以使用 cookie 中的 csrftoken 进行 csrf 验证,由于 cookie 在请求时是放在请求头中的,所以这里使用 ajax 的 headers 来定制请求头,注意请求头的 key 必须为 (X-CSRFtoken),利用 jquery cookie 插件,使用 $.cookie (''csrftoken'') 来获取对应的值即可。

$(function(){
    $(''#btn'').click(function () {
        $.ajax({
            url:''/index'',
            type:''post'',
            headers:{''X-CSRFtoken'':$.cookie(''csrftoken'')},     // 构建请求头
            data:{''user'':$(''#user'').val(),''passwd'':$(''#passwd'').val()},
            success:function () {
                alert(123)
        }
        })
    })
})

为什么是 ''X-CSRFtoken'' 这个名字?

# 1、倒入 csrf中间件查看源码,获取的到底是什么信息
from django.middleware.csrf import CsrfViewMiddleware
 
 
# 2、源码中关于header中的csrf说明,295行(django 1.11)
request_csrf_token = ""
if request.method == "POST":
    try:
        request_csrf_token = request.POST.get(''csrfmiddlewaretoken'', '''')
    except IOError:
        pass
if request_csrf_token == "":
    request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '''')
# 如果用户提交的数据中没有request_csrf_token,那么尝试从meta中获取数据(包含用户提交的所有数据也包括请求头,从中获取settings.CSRF_HEADER_NAME的值用作request_csrf_token)
 
     
# 3、倒入settings查找csrftoken的真正名称
from django.conf import settings
print(settings.CSRF_HEADER_NAME)     # 结果为:HTTP_X_CSRFTOKEN
 
 
# 4、当我们利用ajax定制 request headers时,django会为我们在我们定制的key前面添加HTTP_来进行标识,
# 所以我们实际上传递的key应该为X_CSRFTOKEN,
# 又因为提交数据时不能提交包含下划线的key,会被认为是非法数据,所以,这里的名称为X-CSRFTOKEN
# 官方建议为:X-CSRFtoken

PS:这里的 settings 模块和 settings.py 文件不是一回事儿,settings 模块中包含了 django 很多的默认配置信息,如果我们要对某个配置信息进行修改,可以在 settings.py 中定义,如果没有定义,那么 django 就会按照 settings 中的默认配置进行处理。

扩展信息:

在商城 APP 开发时,在与客户端联调 API 接口的过程中,我们发现,在 PHP 的 $_SERVER 超全局变量中某些自定义的 HEADER 字段居然获取不到。通过抓包工具查看数据包,该自定义头的确是存在的。 后来通过调试我们发现,根本原因是客户端错误地将字段名中的中划线写成了下划线。例如,应该是 X-ACCESS-TOKEN,却被写成了 X_ACCESS_TOKEN。 问题本身很好解决。然而我们想要知道,服务器为何要对字段名中使用了下划线的头视而不见呢?并且,不管是 Apache 还是 Nginx,对于这样的情况,都不约而同地采取了一样的策略。 在 RFC 2616 4.2 节中,有如下一段话: Request (section 5) and Response (section 6) messages use the generic message format of RFC 822 [9] for transferring entities (the payload of the message). 这段话的意思,就是说 HTTP/1.1 的请求和响应消息使用 RFC 822 中的通用消息格式来传输实体(消息载荷)。 在 RFC 822 3.1.2 节中,对于消息格式的说明,有这样一句话: The field-name must be composed of printable ASCII characters (i.e., characters that have values between 33. and 126., decimal, except colon). 也就是说,HEADER 字段名可以可由可打印的 ASCII 字符组成(也就是十进制值在 33 和 126 之间的字符,不含冒号)。 不含冒号很容易理解,因为 Field-Name 和 Value 之间需要用冒号分割。然而,我们通过查询 ASCII 码表可知,下划线的十进制 ASCII 值为 95,也在此范围之内! 其实,在 HEADER 字段名中使用下划线其实是合法的、符合 HTTP 标准的。服务器之所以要默认禁止使用是因为 CGI 历史遗留问题。下划线和中划线都为会被映射为 CGI 系统变量中名中的下划线,这样容易引起混淆。 在 nginx 服务器中,通过显式地设置 underscores_in_headers on 可以开启在字段名中使用下划线。默认该选项是关闭的,所以在默认情况下,所有包含下划线的字段名都会被丢弃。 在我们的开发机中的确也开启了这个选项,为啥还是不能拿到字段名中包含下划线的 HEADER 呢?这是因为我们访问这台开发机的时候,前面还有一层代理服务器,而这台代理服务器没有开启相关选项,导致这种 HEADER 被代理服务器直接丢弃。因此也强烈建议不要在 HEADER 的 Field-Name 中使用下划线。

  当我们对之前的 ajax 提交进行改造时,由于之前没有 csrftoken 的验证,那么如果我们开启验证,那么就需要对所有的 ajax 提交进行修改,那么这里可以使用 ajax 的 setup 进行全局的配置 (可以查看上一篇 ajax 部分内容)。

$(function(){
 
    // 利用ajaxSetup进行全局配置
    $.ajaxSetup({
        beforeSend:function (xhr,settings) {                            // 在发送ajax请求之前,要执行的函数
            xhr.setRequestHeader(''X-CSRFtoken'',$.cookie(''csrftoken''))   // 定制请求头,添加csrftoken
        }
    })
 
    // 其他ajax请求,就无需再定制header发送csrf了。
    $(''#btn'').click(function () {
        $.ajax({
            url:''/index'',
            type:''post'',
            data:{''user'':$(''#user'').val(),''passwd'':$(''#passwd'').val()},
            success:function () {
                alert(123)
        }
        })
    })
})

PS:比如 get,delete 等请求是不需要携带 csrftoken 的,那么我们可以使用在进行全局配置时,对 HTTP 请求方式进行判断。

# 判断HTTP请求方式。针对POST请求提交CSRFtoken
<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    {% csrf_token %}
  
    <input type="button" onclick="Do();"  value="Do it"/>
  
    <script src="/static/plugin/jquery/jquery-1.8.0.js"></script>
    <script src="/static/plugin/jquery/jquery.cookie.js"></script>
    <script type="text/javascript">
        var csrftoken = $.cookie(''csrftoken'');
  
        function csrfSafeMethod(method) {
            // these HTTP methods do not require CSRF protection
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));      # 如过是GET|HEAD|OPTION|TRACE 这四种请求,那么返回False
        }
        $.ajaxSetup({
            beforeSend: function(xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {    # 针对非GET|HEAD|OPTION|TRACE的请求添加CSRFtoken
                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                }
            }
        });
        function Do(){
  
            $.ajax({
                url:"/app01/test/",
                data:{id:1},
                type:''POST'',
                success:function(data){
                    console.log(data);
                }
            });
  
        }
    </script>
</body>
</html>

1.5 CSRF 装饰器

        在我们的网站中,其实并不是所有的站点都需要进行 csrftoken 验证,那么如何对指定的站点函数进行 csrf 验证或者排除呢?

  • @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即 settings 中没有设置全局中间件。
  • @csrf_exempt,取消当前函数防跨站请求伪造功能,即 settings 中设置了全局中间件。

使用 csrf 装饰器需要先进行引用:from django.views.decorators.csrf import csrf_exempt,csrf_protect

from django.shortcuts import render,HttpResponse,redirect
from django.views.decorators.csrf import csrf_exempt,csrf_protect    # 导入csrf装饰器
 
@csrf_exempt                    # 设置该函数取消csrf认证
def index(request):
    error_msg = ''''
    if request.method == ''POST'':
        user = request.POST.get(''user'')
        passwd = request.POST.get(''passwd'')
        if user == ''daxin'' and passwd == ''123456'' or user == ''dachenzi'' and passwd == ''123456'':
            request.session[''username''] = ''daxin''
            request.session[''is_login''] = True
            return redirect(''/page'')
        else:
            error_msg = ''用户名或密码错误''
    return render(request,''index.html'',{''error_msg'':error_msg,''test'':{''name'':''daxin'',''age'':20}})

2 Cookie&Session

        由于 HTTP 协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是 Session. 典型的场景比如购物车,当你点击下单按钮时,由于 HTTP 协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的 Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书。这个 Session 是保存在服务端的,有一个唯一标识。在服务端保存 Session 的方法很多,内存、数据库、文件都有。集群的时候也要考虑 Session 的转移,在大型的网站,一般会有专门的 Session 服务器集群,用来保存用户会话,这个时候 Session 信息都是放在内存的,使用一些缓存服务比如 Memcached 之类的来放 Session。服务端如何识别特定的客户?这个时候 Cookie 就登场了。每次 HTTP 请求的时候,客户端都会发送相应的 Cookie 信息到服务端。实际上大多数的应用都是用 Cookie 来实现 Session 跟踪的,第一次创建 Session 的时候,服务端会在 HTTP 协议中告诉客户端,需要在 Cookie 里面记录一个 Session ID,以后每次请求把这个会话 ID 发送到服务器,我就知道你是谁了。

用一句话总结的话:

  • Session 是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在缓存集群、数据库、文件中。 (服务端存储)
  • Cookie 是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现 Session 的一种方式。 (客户端存储)

2.1 cookie/cookies

        Cookie 可以翻译为 “小甜品,小饼干” ,Cookie 在网络系统中几乎无处不在,当我们浏览以前访问过的网站时,网页中可能会出现 :你好 XXX,这会让我们感觉很亲切,就好像吃了一个小甜品一样。这其实是通过访问主机中的一个文件来实现的,这个文件就是 Cookie。在 Internet 中,Cookie 实际上是指小量信息,是由 Web 服务器创建的,将信息存储在用户计算机上的文件,不同的网站有不同的 cookie,所以又成为 cookies。
        当 Web 服务器创建了 Cookies 后,只要在其有效期内,当用户访问同一个 Web 服务器时,浏览器首先要检查本地的 Cookies,并将其原样发送给 Web 服务器。而当用户结束浏览器会话时,系统将终止所有的 Cookie。
        目前有些 Cookie 是临时的,有些则是持续的。临时的 Cookie 只在浏览器上保存一段规定的时间,一旦超过规定的时间,该 Cookie 就会被系统清除,而持续性的可以永久进行存储。

PS:一个浏览器能创建的 Cookie 数量最多为 300 个,并且每个不能超过 4KB,每个 Web 站点能设置的 Cookie 总数不能超过 20 个。

在 django 中使用 Cookie   现在有这样一个场景,index 页面需要用户首先访问 login 页面,登陆成功后才能访问 index 页面,针对这个需求,如果只使用我们前面介绍的知识是无法进行完成的,而利用 cookie 则非常简单。

# ------------------- views.py -------------------
 
def index(request):
    if request.method == ''GET'':
        username = request.COOKIES.get(''user'')    # 获取用户请求中携带的cookies
        return render(request,''index.html'',{''username'':username})
 
def login(request):
    if request.method == ''GET'':
        return render(request,''login.html'')

    if request.method == ''POST'':
        u = request.POST.get(''username'')
        p = request.POST.get(''password'')
        if u == ''daxin'' and p == ''123123'':
            req = redirect(''/index'')
            req.set_cookie(''user'',u)            # 设置cookie
            return req
        else:
 
            return render(request,''login.html'')

2.1.1 django 中 cookies 用法介绍

针对 cookies,django 提供了设置以及获取的方法,并且还有可选的参数。

# 用变量接受返回用户的信息
response = redirect(''/index'')
response = render(request,''index.html'')
response = Httpresponse(''index'')
 
# 设置cookie
response.set_cookie(''key'',''value'')
 
# 获取cookie
request.COOKIE.get(''key'')

set_cookie 方法还提供了其他的参数用于配置 cookie:

  • key='''':键
  • value='''':值
  • max_age=None:过多少时间超时,单位是秒 (默认是临时生效,重新启动浏览器失效)
  • expires=None:在某个时间后失效,是一个具体的时间。
  • path=''/'':Cookie 生效的路径,/ 表示根路径 (默认),根路径的 cookie 可以被当前站点的任何 url 的页面访问
  • domain=None:Cookie 生效的域名
  • secure=False:https 传输
  • httponly=False:只能 http 协议传输,无法被 JavaScript 获取(不是绝对,底层抓包可以获取到也可以被覆盖)
req.set_cookie(''username'',''abc'',max_age=10,path=''/'',......)

2.1.2 加密的 cookies

        我们在浏览器上可以看到我们指定的 cookie 中的 key 值,这样不是特别安全,那么我们可以对 cookie 进行加密。django 提供 cookie 的加密方法。

req.set_signed_cookie(''name'',''abc.123'',salt=''abc.123'')                 # 设置签名的cookie
request.get_signed_cookie(''name'',salt=''abc.123'',default=''null'')        # 获取签名的cookie

PS:salt 表示要加的签名,获取时,必须要传递对应的签名,才可以正确获取。

2.1.3 基于自定义分页的实例

        通常我们在数据列表页面下面会看到供用户选择的页面数据显示条目数,一旦确定以后,那么以后选择下一页上一页,将会针对用户选择的条目数生成新的页码和数据,那么这个功能是怎么实现的呢?如何才能让浏览器记住用户选择的数据呢?这里通过 cookie 就能实现。

# --------------------  html --------------------
 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .page {
            text-decoration: none;
            margin-right: 3px;
        }
 
        .active {
            background-color: saddlebrown;
            color: white;
        }
    </style>
</head>
<body>
<!-- 模拟数据显示 -->
<ul>
    {% for item in data %}
        <li>{{ item }}</li>
    {% endfor %}
</ul>
 
<div>
    <select name="count" id=''count'' onchange="pageChange(this)">
        <option value="10">10</option>
        <option value="30">30</option>
        <option value="50">50</option>
        <option value="100">50</option>
    </select>
</div>
{{ temp_list | safe }}
 
 
<script src="/static/jquery-3.3.1.min.js"></script>
<script src="/static/jquery.cookie.js"></script>
<script>
    $(function () {
        var val = $.cookie(''page_size'');
        if (val) {
            $(''#count'').val(val);        // 页面加载完毕,赋值页面数量
        }
 
    });
    function pageChange(ths) {
        var val = $(ths).val();          // 获取用户设置的页面条目数
        $.cookie(''page_size'',val);       // 写到cookie中(jquery.cookie),额外的属性可以直接根{''path'':''/'',......}
        location.reload()
    }
</script>
</body>
</html>
 
-------------------- views --------------------

from utils import pagination
 
def page(request):
 
    if request.method == ''GET'':
        page_size = request.COOKIES.get(''page_size'')    # 获取page_size
        current_page = int(request.GET.get(''page'', 1))
        counts = len(all_data)
        if page_size:                   # 如果用户设置了page_size,修改生成的html
            page_obj = pagination.Page(current_page,counts,per_page_count=int(page_size))
        else:
            page_obj = pagination.Page(current_page, counts)
        data = all_data[page_obj.start:page_obj.end]
        temp_list =page_obj.page_str(''/page/'')
 
    return render(request,''page.html'',{''data'':data,''current_page'':current_page,''temp_list'':temp_list})

2.1.4 利用 cookie 进行用户登陆检测

        很多时候,比如我们要访问 index 页面,需要先进行登陆,不登陆那么拒绝访问 index 页面,这种场景下使用 cookie 可以很方便的完成。

def auth(func):
    def index (request,*args,**kwargs):
        if not request.COOKIES.get(''username''):     # 判断cookie中是否存在我们指定的key
            return redirect(''/login'')               # 不存在则跳转回login页面
        return func(request,*args,**kwargs)
    return index

PS:这里利用装饰器的方式对传入的 function 进行 cookie 验证,如果验证失败,那么跳转回 login 页面,在需要 cookie 验证的地方装饰即可

@auth
def index(request):
    ... ...

扩展

  • cookie 会存放在相应头中,应答给用户浏览器的,用户浏览器收到后,会储存 cookie。
  • 上述设置 cookie 的方法其实就是对相应头的设置,如果我们要在相应头中添加额外的数据,那么可以使用如下方式
req = redirect(''/index'')
req.set_signed_cookie(''name'',''abc.123'',salt=''abc.123'',max_age=10,path=''/'')
req[''user''] = ''abc''      # 添加响应头数据
return req

2.2 Session

        之所以要把 cookie 和 session 放在一起说,是因为它俩是有关系的,由于 cookie 是存在用户端,而且它本身存储的尺寸大小也有限,最关键是用户可以是可见的,并可以随意的修改,很不安全。那如何又要安全,又可以方便的全局读取信息呢?于是,这个时候,一种新的存储会话机制:session 诞生了。session 的机制是基于 session_id 的,它用来区分哪几次请求是一个人发出的。为什么要这样呢?因为 HTTP 是无状态无关联的,一个页面可能会被成百上千人访问,而且每个人的用户名是不一样的,那么服务器如何区分相同用户访问的呢?所以就有了找个唯一的 session_id 来绑定一个用户。一个用户在一次会话上就是一个 session_id,这样成千上万的人访问,服务器也能区分到底是谁在访问了。而 session_id,就是通过 cookie 答复给客户端的,所以 session 依赖 cookie,即解决了安全问题,又能在服务端存储用户相关个性化数据。

2.2.1 Django 中的 session

首先根据 session 的工作原理,我们可以大致的分析出服务端对 session 的处理步骤。

服务端设置 session:

  1. 随机生成字符串作为 session_id。
  2. 把 session_id 作为 cookie 返回给用户。
  3. 在某个地方开辟空间存储该 session_id。
  4. 将用户相关信息存入 session_id 对应的字典或者其他数据类型中。

服务端获取 session:

  1. 获取 cookie 中的 session_id
  2. 根据 session_id 获取用户相关的数据

2.2.2 django 中 session 的操作

request 封装的 session,可以使用类似字典的方式进行操作。

2.2.2.1 设置 session

  • request.session [''username''] = u : 仅仅这一步就完成了上面 4 步的功能
  • request.session[''is_login''] = True

2.2.2.2 获取 session

  • request.session[''is_login'']
  • request.session.get(''is_login'',None)
  • request.session.setdefult(''k1'',123)

2.2.2.3 注销时清除用户对应 session 信息

  • request.session.clear (): 不会删除数据库中的 sessionid, 会修改对应 id 的数据 (可能是做了标记,下次登陆会复用这个 sessionid)
  • request.session.delete (request.session.session_key): 会删除数据库中的 sessionid
  • del request.session.get (''username''): 删除 session 中的某个 key 对应的值

2.2.2.4 所有 键、值、键值对

  • request.session.keys()
  • request.session.values()
  • request.session.items()
  • request.session.iterkeys()
  • request.session.itervalues()
  • request.session.iteritems()

2.2.2.5 其他操作

# 用户session的随机字符串
request.session.session_key
  
# 将所有session失效日期小于当前日期的数据删除
request.session.clear_expired()
  
# 检查 用户的随机字符串 在数据库中是否存在
request.session.exists(''session_key'')
  
# 删除当前用户的所有Session数据
request.session.delete(''session_key'')
  
# 清空所有session
request.session.clear()
 
# django中的session默认有效期为两周,单独设置某个session的超时时间需要进行如下修改
request.session.set_expiry(value)
  # 如果value是个整数(或者是个乘法表达式 60 * 60),session会在些秒数后失效。
  # 如果value是个datatime或timedelta,session就会在这个时间后失效。
  # 如果value是0,用户关闭浏览器session就会失效。
  # 如果value是None,session会依赖全局session失效策略。  

PS:如果我们在 session 中写入了用户名称,需要在页面上展示,并不需要在后端获取 (request.session.get (''username'')),在后在前端渲染 ({{ username }}), 因为我们渲染页面的同时,传递了 request 对象,request 对象又包涵了我们应答的所有信息,所以可以在页面上直接利用 {{ request.session.username }} 进行渲染。

2.2.3 使用 session 进行登陆验证

上面使用 cookie 对主页进行了登陆验证,这里使用 session 配合装饰器,完成页面登陆验证。

def auth(func):
    def inner(request,*args,**kwargs):
        if request.session.get(''is_login'',None):
            return func(request,*args,**kwargs)
        else:
            return redirect(''/login/'')
 
    return inner
 
@auth
def index(request):
 
    if request.method == ''GET'':
        return render(request,''index.html'',{''username'':request.session[''username'']})
 
def login(request):
 
    if request.method == ''GET'':
        return render(request,''login.html'')
 
    if request.method == ''POST'':
        u = request.POST.get(''username'')
        p = request.POST.get(''password'')
 
        if u == ''daxin'' and p == ''123123'':
            request.session[''username''] = u
            request.session[''is_login''] = True
            return redirect(''/index/'')
        else:
            return render(request,''login.html'')

PS:django 默认会把 session_id 和数据的对应关系放在数据库中,一般存放在 django_session 表中,如果是新建的项目,需要先执行如下命令生成表才可以使用。

python3 manage.py makemigrations
python3 manage.py migrate

2.2.4 全局配置 session

默认情况下 session 是有默认配置的,比如默认超时时间为两周,如果我们要修改全局配置,那么需要在 settings.py 中进行配置

SESSION_COOKIE_NAME = "sessionid"                        # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/"                                # Session的cookie保存的路径
SESSION_COOKIE_DOMAIN = None                              # Session的cookie保存的域名
SESSION_COOKIE_SECURE = False                             # 是否Https传输cookie
SESSION_COOKIE_HTTPONLY = True                            # 是否Session的cookie只支持http传输
SESSION_COOKIE_AGE = 1209600                              # Session的cookie失效日期(2周)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False                   # 是否关闭浏览器使得Session过期
SESSION_SAVE_EVERY_REQUEST = False                        # 是否每次请求都保存Session,默认修改之后才保存,建议修改为True

PS:上面的参数都是默认配置,如果需要修改单个配置,只需要添加对应的 key 和 value 就可以了。 

很多时候基于业务的需求,我们需要把 session 放在别的地方,比如缓存或者文件中,那么就需要使用如下配置了

SESSION_ENGINE = ''django.contrib.sessions.backends.db''     # 默认配置,表示存放在数据库中
SESSION_ENGINE = ''django.contrib.sessions.backends.cache''  # 使用cache进行存储
SESSION_CACHE_ALIAS = ''default''                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置

上面 cache_alias 其实是我们在配置文件中指定的 chache 的别名,因为我们如果要写到 memcahed 中,那么就需要写连接地址以及端口,那么就需要在 cache 中进行定义

CACHES = {
    ''default'': {      # 这里就表示cache的别名
        ''BACKEND'': ''django.core.cache.backends.memcached.MemcachedCache'',
        ''LOCATION'': [
            ''172.19.26.240:11211'',
            ''172.19.26.242:11211'',
        ]
    }
}

PS:django 默认支持 memcached,不支持 redis,需要安装其他插件。下面是缓存到文件中去

''django.contrib.sessions.backends.file''    # 引擎
SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()    
# 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T

cookie与session,Django中间件,跨站请求伪造

cookie与session,Django中间件,跨站请求伪造

cookie与session,Django中间件,跨站请求伪造

一.cookie与session

Cookie的由来

因为HTTP协议是无状态的,就是说你没次访问一个网站就像是第一次访问.因此就诞生的Cookie.

什么是Cookie Cookie具体指的是一小段信息,它是由服务器发出来的存储在浏览器上的一组组的键值对,当你的浏览器下次再访问时,就会携带这些信息,以便服务器来提取有用的信息.

Cookie

cookie的工作原理: 由服务器产生的内容,浏览器收到请求后保存在本地,当浏览器再次访问时,浏览器会自动的带上cookie,这样服务器通过cookie来判定,你是不是来过.

二.Django中操作Cookie

获取Cookie

request.COOKIES[''key'']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='''', max_age=None)

参数:

  • default: 默认值
  • salt: 加密盐
  • max_age: 后台控制过期时间

设置Cookie

rep = HttpResponse(...)
rep = render(request, ...)

rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt=''加密盐'', max_age=None,...)

参数:

  • key: 键
  • value: 值
  • max_age=None: 超时时间

删除Cookie

def logout(request):
	rep = redirect("/login/")
	rep.delete_cookie("user") #删除用户浏览器上之前设置的usercookie值
	return rep

Cookie实现登录认证

def login(request):
    if request.method == ''POST'':
        username = request.POST.get(''username'')
        password = request.POST.get(''password'')
        if username == ''kang'' and password == ''123'':
            old_path = request.GET.get(''next'')  # 拿到要访问的页面
            if old_path:
                obj = redirect(old_path)   #如果有就返回那个页面
            else:
                obj = redirect(''/home/'')
            obj.set_cookie(''name'',''kang'')  # 让客户端浏览器记录一个键值对
            return obj

    return render(request, ''login.html'')

# 登录装饰器
from functools import wraps
def login_auth(func):
    @wraps(func)
    def inner(request,*args, **kwargs):
        if request.COOKIES.get(''name''):  #判断有没有cookie,有的话直接返回
            res = func(request,*args, **kwargs)
            return res
        else:
            target_url = request.path_info # 没有的跳转到登录页,并且附带上想要访问的
            return redirect(''/login/?next=%s''%target_url)
    return inner


@login_auth
def home(request):
    return HttpResponse(''我是主页面,登录后查看'')

@login_auth
def index(request):
    return HttpResponse(''我是买东西页面'')

@login_auth
def play(request):
    return HttpResponse(''我是玩游戏页面'')

@login_auth
def logout(request):
    obj = redirect(''/login/'')
    obj.delete_cookie(''name'')
    return obj

Session

Session的由来

  1. Cookie虽然能够帮助我们解决需求,但是他本身最大支持4096字节,因为他是存在客户端的,所以也有可能被拦截或者窃取.因此出现了Session,它是存放在服务器端,并且有较高的安全性.
  2. 基于HTTP协议的无状态特征,服务器根本就不知道访问者,那么Cookie就起到了桥接的作用.
  3. 我们可以给没个客服端的Cookie分配一个唯一的id,这样在访问时,通过Cookie,服务器就知道来的人是''谁'',然后在根据不同的Cookie的id,在服务器上保存一段时间的信息.如"账号密码".
  4. Cookie弥补了HTTP无状态的不足,让服务器知道了来的人是谁,但是Cookie是以文本的形式保存在本地.自身安全性较差,所以我们就通过Cookie识别不同的用户,对应的在Session保存私密的信息以及超过4096的字节文本.
  5. 所有说,Cookie和Session是共通性的东西.

Django中的Session相关方法

# 设置,获取,删除session中的数据

#设置
request.session[''username''] = ''kang''

#获取
request.session.get(''username'')

#删除
request.session.flush()

#设置会话Session和Cookie的超时时间
request.session.set_expiry(value)
  如果value是个整数,session会在这些秒数后失效
  如果value是0,用户关闭浏览器就会失效.

Session实现登录认证


Django中间件

中间件的作用

  • 用户访问频率限制
  • 用户是否是黑名单,白名单
  • 所有用户登录校验
  • 只要是涉及到网址全局的功能,你就应该考虑使用中间件.

django中间件暴露给程序员的五个可以自定义的方法(五个方法都是在特定的条件下自定触发的)

1.你要新建一个文件夹, 里面可以建立任意名称的py文件
里面写类固定继承
from django.utils.deprecation import MiddlewareMixin
class MyThink(MiddlewareMixin):
#五个方法:
需要掌握的:
1. process_request(******):请求来的时候 会从上往下依次经过每一个中间件里面process_request,一旦里面返回了HttpResponse对象那么就不再往后执行了,会执行同一级别的process_response
             def process_request(self,request):
                 print(''我是第一个自定义中间件里面的process_request方法'')
                 return HttpResponse("我是第一个自定义中间件里面的HttpResponse对象返回值")  # 直接原地返回
2.process_response(***):响应走的时候 会从下往上依次进过每一个中间件里面的process_response
              def process_response(self,request,response):  # response就是要返回给用户的数据
                  print("我是第一个自定义中间件里面的process_response方法")
                  return response
了解的:
                process_view:路由匹配成功之后执行视图函数之前触发
                process_exception:当视图函数出现异常(bug)的时候自动触发
                process_template_response:当视图函数执行完毕之后并且返回的对象中含有render方法的情况下才会触发
         

跨站请求伪造(钓鱼网站)

就相当于你搭建了一个和充Q币一模一样的网站,用户在你的网站进行充值的时候输入的是自己的结果充给了你.

简单的原理

  • 在你写form表单的时候,用户的用户名,密码都会真实的提交给Q币后台
  • 但是被充的人的账户却不是用户填的,你暴露给用户的是一个没有name属性的input框
  • 你自己写好了一个隐藏的带有的name和value的input框

解决钓鱼网站的策略

  • 只要是用户想要提交post请求的页面,我在返回给用户的时候就提前设置好一个随机字符串
  • 当用户提交post请求的时候,我会自动先获取查找是否有该随机的字符串.
  • 如果有正常提交,否则直接报403

form表单

你在写form表单的时候只需要加上
  {% csrf_token %} 

ajax

第一种: 自己在页面上先通过{% cstf_token %}获取到随机字符串,然后利用标签查找
data: {''username'':''kang'',''csrfmiddlewaretoke:$([name="csrfmiddlewaretoken"]'').val()}

第二种:
data:{''username'':''kang'', ''csrfmiddlewaretoken'':''{{ csrf_token }}''}

第三种
拷贝js文件

如何不校验使用装饰器

from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator # 它作用于CBV

@csrf_exempt  # 避免校验
def exem(request):
    return HttpResponse(''exempt'')

@csrf_protect  # 可单独校验
def pro(request):
    return HttpResponse(''pro'')



CBV
from django.views import View
# 第一种
# @method_decorator(csrf_exempt,name=''dispatch'')
@method_decorator(csrf_protect,name=''post'')
class MyCsrf(View):
    # 第二种
    # @method_decorator(csrf_exempt)
    @method_decorator(csrf_protect)
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request,*args,**kwargs)
    def get(self,request):
        return HttpResponse(''hahaha'')

    @method_decorator(csrf_protect)
    def post(self,request):
        return HttpResponse(''post'')




cookie和session django中间件

cookie和session django中间件

[TOC]

一、cookie和session

1. 为什么要有cookie和session

  • 目的是为了保存客户端的用户状态
  • http协议是无状态的

二、cookie

1. 什么是cookie

  • 简单来说,cookie就是保存在客户端浏览器上的键值对
  • 浏览器中保存的cookie即这些键值对是由服务端设置的,再保存到客户端浏览器上。浏览器有权禁止cookie的写入(就是浏览器不保存cookie)

2. django中关于cookie的使用

(1)后端设置cookie

  • 之前在django后端的视图层中,视图函数最后返回的都是HttpResponse对象,但现在,我们操作cookie,则需要使用HttpResponse对象来设置cookie
  • 通过HttpResponse对象.set_cookie(''key'',''value'')语句来设置cookie
  • 实例:在登陆成功时设置cookie
def login(request):
	if request.method == ''POST'':
        username = request.POST.get(''username'')
        password = request.POST.get(''password'')
        if username == ''jason'' and password == ''123'':
            # 登录成功
            old_path = request.GET.get(''next'')  # 登陆成功后要跳转的url(使用登陆认证装饰器在登陆的url后面携带get请求参数——》 ?next=url)
            if old_path:  # 防止用户直接访问的login页面
                obj = redirect(old_path)
            else:
                obj = redirect(''/home/'')  # 默认跳转到首页 当用户没有想要访问的网址
            obj.set_cookie(''whoami'',''jason'')  # 告诉浏览器保存一个键值对
            return obj
    return render(request,''login.html'')

(2)后端获取cookie

  • 通过request.COOKIES.get(key)语句来获取cookie

  • 实例:设计登陆认证装饰器

from functools import wraps  
def login_auth(func):
    @wraps(func)  # wraps作用是 修复被装饰的函数
    def inner(request,*args,**kwargs):
        # 判断当前用户是否登录
        # print(''request.path_info:'',request.path_info)  # 只获取url
        # print(''request.get_full_path():'',request.get_full_path())  # url+get请求的参数
        if request.COOKIES.get(''whoami''):
            res = func(request,*args,**kwargs)
            return res
        else:
            target_path = request.path_info
            return redirect(''/login/?next=%s''%target_path)
    return inner

(3)设置cookie的超时时间

  • cookie的超时时间是指该cookie在浏览器上能够保存的时间,从该cookie在浏览器上出现开始计算。
  • 设置语法:
    • HttpResponse对象.set_cookie(''key'',''value'',max_age=n)
    • HttpResponse对象.set_cookie(''key'',''value'',expires=n)
      • 最后的一个参数都是设置超时时间,并且都是以秒为单位
      • 区别:如果你要给IE浏览器设置cookie的超时时间,你只能用expires

(4)删除cookie

  • 删除cookie会让浏览器立马清楚其保存的响应的键值对

  • 删除语法:HttpResponse对象.delete_cookie(key)

  • 实例:登出/注销功能

@login_auth
def logout(request):
    obj = redirect(''/home/'')
    obj.delete_cookie(''whoami'')
    return obj

三、session

1. 什么是session

  • session是保存在服务端上的键值对,在客户端的cookie中也保留一个session的键。
  • session的工作机制需要依赖于cookie
    • 客户端浏览器上的cookies的其中之一的一个键值对是sessionid:session的键生产的随机字符串
  • session的键其实是世界上唯一的一个字符串,值是我们自定义的一些数据。虽然在我们设置session时,自己给出了一个任意的字符串当做session的key,但是django后端会自动将该key转变成一个世界上唯一的字符串,并且还将这个key对应的值进行加密,最后默认保存在数据库django_session表中(当然也可以通过修改一些设置,让session保存在其他本地文本文件中或内存中)。

2. django中关于session的使用

(1) 设置session

  • 设置session语法:request.session[key] = value

    • 第一次设置的时候可能会报错,是因为你没有执行数据库迁移命令生成django需要用到的一些默认表(django_session)
  • django默认的session失效时间是14天

  • 当执行request.session[key] = value这句语句时发生的事:

    1. djang内部自动帮你调用算法生成一个随机的字符串作为session的key,并把该key对应的值进行加密
    2. 在django_session表中添加数据(数据保存方式: 随机字符串 加密后的数据 失效时间)
    3. 将产生的随机字符串返回给客户端浏览器 让浏览器保存
  • 注意:一个项目中,其数据库的django_session表中,无论有多少个session,通过同一种浏览器设置的session只会保存在一行记录中。不同浏览器设置的session才会保存在不同的行中。

(2)获取session

  • 语法:request.session.get(key)key是我们之前设置session时,指定的key,不是生成的那个随机字符串。
  • 当执行request.session.get(key)时发生的事:
    1. django内部会自动去请求头里面获取cookie
    2. 拿着sessionid所对应的随机字符串去django_sessoion表中一一比对
    3. 如果比对上了,会将随机字符串对应的数据获取出来,自动放入request.session中供程序员调用,如果没有就是一个空字典

(3)设置失效时间

  • 语法:request.session.set_expiry(value)

    • 如果value是个整数,session会在些秒数后失效。
    • 如果value是个datatime或timedelta,session就会在这个时间后失效。
    • 如果value是0,用户关闭浏览器session就会失效。
    • 如果value是None,session会依赖全局session失效策略。
  • 失效时间是指到了这个时间,保存在服务端的django_session表中的这个session数据就没用了。之后会被删除。且客户端浏览器上的sessionid的这个cookie也会失效。

(4)删除session

  • 语法:
    • request.session.delete()
      • 客户端和服务端的关于session的会全部删除,数据库中只会根据浏览器的不同删对应的数据
    • request.session.flush()
      • 该方法只会删除浏览器上的sessionid的那个cookie,服务端的只会到了失效时间后再删除(建议使用这个)

四、token

  • 也比较常用的一种类似session的东西
  • 点我查看token详情

五、django中间件

  • django中间件的作用非常强大,请求数据到后端要经过中间件,视图函数响应的数据(return返回的数据)也要经过中间件。
  • 数据在通过中间件时,中间件都有各自独立的功能,在特定情况下对数据做相应的处理。
  • django默认有七个中间件
  • django支持自定义中间件
  • django中间件的作用
    1. 全局的用户登录校验
    2. 全局的用户访问频率校验
    3. 全局的用户权限校验()

1. 数据通过django中间件的流程

(1)正常流程

  • 请求数据到django后端的django中间件后,按照从上到下的顺序依次通过settings文件中MIDDLEWARE中书写的各个中间件——》
  • 数据到视图层,视图层再返回响应数据——》
  • 响应数据再按照从下到上的顺序依次通过settings文件中MIDDLEWARE中书写的各个中间件。

(2)特殊情况

  • 当中间件中的process_request 方法返回了一个HttpResponse对象,那么请求会立刻停止往后面走(下面的中间件和之后的视图函数都不走),立即原路返回

2. django中间件中常用的方法

  • django各个中间件中一般都含有一个process_request process_response 方法
  • 这些方法都含有request形参,放在self之后
  • 如果形参中含有response 那么必须要返回,若返回其他,则响应数据就会变成其他。

(1)process_request

  • 请求来的时候会按照settings配置文件中从上往下的顺序,依次执行每一个中间件内部定义的process_request方法

  • 当请求数据走到中间件时,会执行该方法。若该中间件没有该方法,则会跳过该中间件继续走下一个中间件

  • 该方法一旦返回了HttpResponse对象,那么请求会立刻停止往后走,立即原路返回(当process_request方法直接返回HttpResponse对象之后,会直接从当前中间件里面的process_respone往回走)

  • 方法参数:

    def process_request(self,request):
    

(2)process_response

  • 视图函数返回的响应数据走的时候,会按照settings配置文件中从下往上的顺序,依次执行每一个中间件内部定义的process_response方法

  • 该方法必须有两个形参request,response,并且必须返回response形参,不返回直接报错

  • 该方法返回什么(HttpResponsed对象) 前端就能获得什么,即该方法返回的数据会覆盖掉之前视图函数返回的响应数据。并且若该中间件上面的中间件的process_response方法也返回了HttpResponsed对象,则会再把该中间件的返回数据再覆盖掉。

  • 方法参数:

    def process_response(self,request,response):
    

(3)process_view

  • 路由匹配成功之后执行视图函数之前触发该方法

  • 如果该方法返回了HttpResponse对象 那么会从下往上一次经过每一个中间件里面的process_response方法

  • 方法参数:

    def process_view(self,request,view_name,*args,**kwargs):
    
    # view_name 是要执行的视图函数的内存地址  
    

(4)process_template_response

  • 当你返回的对象中含有render属性指向的是一个render方法的时候才会触发,从下往上的顺序

  • 方法参数:

    def process_template_response(self, request, response):
    
  • 实例

# 让后端视图函数中返回的对象含有render属性的方式
def mdzz(request):
	print(''我是视图函数mdzz'')  
	def render():
		return HttpResponse(''你好呀 我是奇葩'')
	obj = HttpResponse(''我很好 好的像个傻逼一样'')
	obj.render = render
	return obj

# 响应数据变成了: 我是视图函数mdzz

(5)process_exception

  • 当视图函数中出现错误,会自动触发,顺序是从下往上

  • 方法参数:

    def process_exception(self, request, exception):
    

3. 自定义中间件

  • 注意:django暴露给用户五个可以自定义的方法(就是上面提到的5个django中间件常用的方法)来自定义中间件

(1)自定义中间件的步骤

  1. 在应用文件下(如:app01)新建一个任意名字的文件夹(如:mymddleware)

  2. 在mymddleware文件下新建一个任意名字的py文件(如:mymdd.py)

  3. 导入方法

    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse,render,redirect
    
  4. 在mymdd文件中自定义类,一个类就是一个中间件。类内部写上面提到的5个django中间件中常用的方法

  5. 在settings文件的MIDDLEWARE变量值中添加自定义的中间件路径

    MIDDLEWARE = [
        ''django.middleware.security.SecurityMiddleware'',
        ''django.contrib.sessions.middleware.SessionMiddleware'',
        ''django.middleware.common.CommonMiddleware'',
        ''django.middleware.csrf.CsrfViewMiddleware'',
        ''django.contrib.auth.middleware.AuthenticationMiddleware'',
        ''django.contrib.messages.middleware.MessageMiddleware'',
        ''django.middleware.clickjacking.XFrameOptionsMiddleware'',
        ''app01.mymiddleware.mymdd.MyMdd1'',  # 添加自定义中间件
        ''app01.mymiddleware.mymdd.MyMdd2'',  # 添加自定义中间件
    ]
    

(2)自定义django中间件实例

class MyMdd1(MiddlewareMixin):
    def process_request(self,request):
        print(''我是第一个中间件里面的process_request方法'')
        # return HttpResponse("我是中间件一里面的")

    def process_response(self,request,response):
        """
        :param request:
        :param response: 就是后端返回给前端的数据
        :return:
        """
        print(''我是第一个中间件里面的process_response方法'')
        return response
        # return HttpResponse("我是中间件一里面的")

    def process_view(self,request,view_name,*args,**kwargs):
        print(view_name)
        print(args)
        print(kwargs)
        print(''我是第一个中间件里面的process_view方法'')
        # return HttpResponse("我是中间件一里面的process_view")

    def process_exception(self,request,exception):
        print(''我是第一个中间件里面的process_exception'')
        print(exception)

    def process_template_response(self, request, response):
        print(''我是第一个中间件里面的奇葩方法'')
        return response
    
class MyMdd2(MiddlewareMixin):
    def process_request(self,request):
        print(''我是第二个中间件里面的process_request方法'')
        # return HttpResponse(''我是中间件二里面的'')

    def process_response(self,request,response):
        """
        :param request:
        :param response: 就是后端返回给前端的数据
        :return:
        """
        print(''我是第二个中间件里面的process_response方法'')
        return response

        # return HttpResponse("我是中间件二里面的")
    def process_view(self,request,view_name,*args,**kwargs):
        print(view_name)
        print(args)
        print(kwargs)
        print(''我是第二个中间件里面的process_view方法'')
        # return HttpResponse("我是中间件二里面的process_view")

    def process_exception(self, request, exception):
        print(''我是第二个中间件里面的process_exception'')
        print(exception)

    def process_template_response(self, request, response):
        print(''我是第二个中间件里面的奇葩方法'')
        return response    

cookie操作, session操作, django中间件

cookie操作, session操作, django中间件

[TOC]

cookie, session, 及token的工作原理

cookie与session

为什么要有这些技术?

  • HTTP协议是无状态的, cookie和session是为了保持客户端的用户状态

cookie:

  • 保存在客户端浏览器上的键值对,
  • cookie虽然是保存在客户端浏览器上的键值对, 但它是由服务端设置的
  • 浏览器有权禁止cookie的写入

session:

  • 保存在服务端上面的键值对, session的工作机制是需要依赖于cookie的

token

  1. 加密算法, salt: xxx
  2. 用户信息 + xxx --> 随机字符串
  3. 用户信息 + 随机字符串 --> res, 浏览器的cookie保存res
  4. 之后浏览器发送cookie, 服务端将res拆分成: 用户信息 + 随机字符串
  5. 服务端将用户信息通过相同的加密算法得出一个结果与拆分出的随机字符串进行比对

优点: 一定程度保证了用户信息安全, 同时服务端不需要使用session存储用户信息

cookie基本操作

orm操作cookie

  • 通过HttpResponse对象给浏览器设置cookie: obj.set_cookie(''k'', ''v'')
  • 通过request.COOKIES.get(''k'')获取浏览器携带的cookie值
  • 设置cookie的超时时间, max_age, expires(针对于IE浏览器)
''''''
def login(request):
    if request.method == ''POST'':
        username = request.POST.get(''username'')
        password = request.POST.get(''password'')
        if username == ''jason'' and password == ''123'':
            obj = redirect(''/home/'')
            obj.set_cookie(''user'', ''jason'', expires=10)  # expires参数的值以秒为单位
            return obj

    return render(request, ''login.html'')


def home(request):
    if request.COOKIES.get(''user''):
        return HttpResponse(''登录成功'')
        
    return redirect(''/login/'')
''''''

基于cookie实现登录认证

''''''
from django.shortcuts import render, HttpResponse, redirect
from functools import wraps


def login_auth(func):
    @wraps(func)
    def inner(request, *args, **kwargs):
        # 访问 http://127.0.0.1:8000/reg/?username=jason
        # print(request.path_info)  # /reg/, 只获取GET请求中的url
        # print(request.get_full_path())  # /reg/?username=jason, 获取GET请求中的url + url后面携带的参数

        # 校验用户是否登录
        if request.COOKIES.get(''user''):
            res = func(request, *args, **kwargs)
            return res
        else:  # 用户未登录跳转到login页面, 同时在跳转到login的GET请求中携带原来想要访问的url
            target_path = request.path_info
            return redirect(''/login/?next=%s'' % (target_path,))

    return inner


def login(request):
    ...

            old_path = request.GET.get(''next'')  # GET请求url后的携带参数一字典形式存放到request.GET中
            if old_path:  # 用户未登状态下录访问了本服务器的非login页面
                obj = redirect(old_path)
            else:  # 用户直接访问本服务器的login页面
                obj = HttpResponse(''登录成功!'')

            obj.set_cookie(''user'', ''jason'')

            return obj

    return render(request, ''login.html'')


@login_auth
def index(request):
    return HttpResponse(''登录成功! 开始访问index页面'')


@login_auth
def shopping(request):
    return HttpResponse(''登录成功! 开始访问shopping页面'')


@login_auth
def logout(request):
    obj = HttpResponse(''注销成功, 已删除cookie!'')  # 删除cookie
    obj.delete_cookie(''user'')
    return obj

''''''

session基本操作

  • 设置session: request.session[''k''] = ''v''

    1. django内部自动调用算法随机生成两个加密字符串, 一个对应"k", 一个对应"v"
    2. 在django_session表中的session_key字段插入"k"对应的加密字符串, session_data字段插入"v"对应的加密字符串
    3. 将"k"对应的加密字符串返回给前端, 浏览器中的cookie以 sessionid为key保存该字符串
  • django_session表中的expire_date默认为2周

  • 获取session: request.session.get(''k'')

    1. django内部会自动取请求头里获取cookie
    2. 拿着cooke中的sessionid所对应的加密字符串到django_session表中一一比对
    3. 如果比对上了, 会将: "k"对应的加密字符串, 以及{''k'': ''v''}放到request.session对象中
  • django的session在创建数据的时候是针对浏览器的, 同一个浏览器只会在django_session表中生成一条数据

  • 删除session只删除对应浏览器的session

  • 能够作为数据库的有哪些

    ​ 数据库软件

    ​ 关系型 ​ 非关系型 ​ 文件 ​ 内存

def set_session(request):
    request.session[''k''] = ''v''
    request.session[''l''] = ''w''
    request.session.set_expiry(60)
    return HttpResponse(''session设值成功!'')
    # 报错: no such table: django_session, 解决: 执行数据库迁移命令, 会自动创建django_session表


def get_session(request):
    res = request.session.get(''k'')
    
    if res:
        print(request.session)  # <... object at 0x000001A7B1402940>
        
        print(request.session.__dict__)
        # {''...session_key'': ''vp3...'', ''_session_cache'': {''k'': ''v'', ''l'': ''w'', ''_session_expiry'': 60}}
        
        return HttpResponse(''获取session值: %s'' % (res,))  # 获取session值: v
    
    else:
        return HttpResponse(''session失效了!'')


def delete_session(request):
    # request.session.delete()  # 删除服务端session, 不删除客户端session
    request.session.flush()  # 同时删除客户端和服务端的session
    return HttpResponse(''成功删除session值!'')

django中间件

django默认的七个中间件都有各自独立的功能

Scroll from Source按钮, 定位到当前文件具体所在的文件信息

django支持自定义中间件, 有五个用户可以自定义的方法, 这五个方法会在特定阶段自动触发

需要掌握的方法

  • process_request:
    • GET请求来时按照settings配置文件中从上往下依次执行每一个中间件内部定义的process_request方法
    • 如果中间件内部没有改方法, 会直接跳过
    • 该方法如果返回了HttpResponse对象, 那么GET请求会立即停止, 然后原路返回
    • 如果要做网站的全局性功能, 可以使用django中间件, 对象 + 字符串 --> 反射
      • 全局性的用户登录校验
      • 全局的用户访问频率校验(反爬措施)
      • 全局的用户权限校验
  • process_response
    • 响应走的时候按照settings配置文件中从下往上依次执行每一个中间件内部定义的process_response方法
    • 该方法有两个形参, 并且必须返回response形参, 否则会报错
    • 如果process_request方法返回了HttpResponse对象, 会直接从当前中间件中的process_response方法往回走, 没有执行的中间件不再执行
    • 该方法返回怎样的HttpResponse对象, 前端就会获得对应的数据
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class MyMdd1(MiddlewareMixin):
    def process_request(self, request):
        print(''第一个中间件中的process_request方法'')
        # return HttpResponse(''第一个中间件中的process_request方法的返回值'')

    def process_response(self, request, response):
        """
        :param request:
        :param response: 后端返回给前端的数据
        :return:
        """
        print(''第一个中间件中的process_response方法'')
        return response
        # return HttpResponse(''第一个中间件中的process_response方法返回的HttpResponse对象'')

    def process_view(self, request, view_func, *view_args, **view_kwargs):
        print(view_func)  # <function mdzz at 0x00000243C024CA60>
        print(view_args)  # ((), {})
        print(view_kwargs)  # {}
        print(''第一个中间件中的process_view方法'')

    def process_exception(self, request, exception):
        print(''第一个中间件中的process_exception方法'')
        print(exception)  # name ''username'' is not defined

了解的方法, 能简单口述即可

  • process_view
    • 路由匹配成功之后, 视图函数执行之前触发该方法
  • process_templates_response
    • 如果形参中有response, 则必须要将其返回
    • 当返回的对象含有render属性, 且该属性指向一个render方法时触发
  • process_exception
    • 当视图函数中出现错误会自动触发, 顺序是从上往下

课外拓展题: RBAC

Role-Based Access Control: 基于角色的访问权限控制

  1. 用户登录是查询出其可以访问的url信息,
  2. 使用session存储url权限信息,
  3. 并将其返回给前端cookie
  4. 用户再次请求时, 使用request.path_info获取其访问的url与session中的url权限信息进行比对, 实现用户权限管理
''''''
class User:
		username
		password
		
		1           jason	
		2           egon
		3           owen
		
	
	class Permission:
		title  # 权限名
		url  # 权限对应的url
		
		添加用户				/add_user/
		查看用户				/check_user/
		编辑用户				/edit_user/\d+/
		删除用户				/delete_user/\d+/
		
	
	class User2Permission:
		user
		permission
		
		id             user_id         permission_id
		1				1				1
		2				1				2
		3				1				3
		4				1				4
		
		
	CRM项目   6  8   客户关系管理系统
	
	
		如何优化上面的权限管理     》》》     RBAC
		
		白名单  不设置任何权限校验的
''''''

今天的关于cookie,session,django中间件,csrf回顾scrapy cookie中间件的分享已经结束,谢谢您的关注,如果想了解更多关于5 - django-csrf-session&cookie、cookie与session,Django中间件,跨站请求伪造、cookie和session django中间件、cookie操作, session操作, django中间件的相关知识,请在本站进行查询。

本文标签: