django 中间件和csrf
django操作cookie
# cookie 常见参数
set_signed_cookie(key,value,salt='加密盐')
set_signed_cookie(key,value,max_age=超时时间:默认是秒数)
expires:专门针对IE浏览器设置超时时间
    
# 删除cookie
HttpResponse对象.delete_cookie(key) 
django操作session
# session
session数据是保存在服务端的(存?),给客户端返回的是一个随机字符串
sessionid	: 随机字符串
# 在默认情况下操作session的时候需要django默认的一张django_session表
当我们执行数据库迁移命令
django会自己创建很多表 django_session就是其中的一张
# django默认session过期时间
django默认的session的过期时间是14天
但是你也可以人为的修改它
# 操作session格式
设置
request.session['key'] = value  # 可以设置多组
获取
request.session.get('key')  # 可以获取多组
设置session内部工作原理
# 设置session
def set_session(request):
    request.session['name'] = 'jason'
    return HttpResponse('设置成功')
# 分析
1.django内部会自动帮你生成一个随机字符串
2.django内部自动将随机字符串和对应的数据存储到django_session表中
	中间件第二个执行:
	2.1 先在内存中产生操作数据的缓存
	2.2 在响应经过django中间件的时候才真正的操作数据库
    
3.将产生的随机字符串返回给客户端浏览器保存
获取session内部工作原理
# 获取session
def get_session(request):
    print(request.session.get('name'))
    return HttpResponse('获取成功')
# 分析
1.自动从浏览器请求中获取session对应的随机字符串
2.拿着该随机字符串去django_session表中查找对应的数据
3.如果比对上了自动获取并'解密处理' 将对应的数据取出并以字典的形式封装到request.session中
4.如果比对不上 则request.session.get()返回是None
django操作session参数
# 获取产生的随机字符串
request.session.session_key  
# 只删客户端
request.session.delete() 
# 服务端 客户端都删
request.session.flush()  
# 删除session
def del_session(request):
    request.session.delete()
    return HttpResponse('删除')
session 设置过期时间
'request.session.set_expiry() 设置时间'
# set_expiry() 括号内可以放四种类型的参数:
1.整数						 多少秒
2.日期对象			   			到指定日期就失效
3.0							   一旦当前浏览器窗口关闭立刻失效
4.不写						 失效时间就取决于django内部全局session默认的失效时间
# 设置过期时间的使用
'设置session'
def set_session(request):
    # 设置session
    request.session['name'] = 'jason'
    # 设置过期时间
    request.session.set_expiry(3)
    return HttpResponse('设置了')
'获取session'
def get_session(request):
    # 判断session
    if request.session.get('name'):
        print(request.session)
        print(request.session.get('name'))
        return HttpResponse('获取了')
    return HttpResponse('失效,未获取')
session数据的存储位置
1.数据库存储
2.缓存存储
3.文件存储
4.缓存+数据库存储
5.动态加密

CBV添加装饰器
'''
CBV中django不建议直接给类的方法加装饰器
CBC添加装饰器的三种方法
'''
# 需要借助于一个专门的装饰器模块
from django.utils.decorators import method_decorator
# 方式1:直接在类中的某个方法上添加
class MyLoginView(views.View):
    @method_decorator(login_auth)
    def get(self, request):
        return HttpResponse("from CBV get view")
    
# 方式2:直接在类名上添加并指定
'可以添加多个针对不同的方法加不同的装饰器'
@method_decorator(login_auth, name='get')
@method_decorator(login_auth, name='post')
class MyLoginView(views.View):
    def get(self, request):
        return HttpResponse("from CBV get view")
    
# 方式3:重写dispatch方法并添加作用于类中所有的方法
class MyLoginView(views.View):
    @method_decorator(login_auth)
    def dispatch(self, request, *args, **kwargs):
        super().dispatch(request,*args,**kwargs)
django中间件
# 中间件的简介
django自带7个中间件,每个中间件都有各自的功能
django还支持程序员自定义中间件
'''
在用django开发项目的项目的时候
只要涉及到全局相关功能都可以使用中间件方便完成
'''
# 中间件是django的门户
1请求来的时候需要先经过中间件才能到达真正的django后端
2响应走的时候最后也需要经过中间件才能发送出去
# 中间件使用常见场景
'只要涉及到项目全局的功能,一定要想到中间件'
用户黑名单校验
用户访问频率校验
网站全局用户身份校验
# 自定义中间件
process_request
process_response
process_view
process_template_response
process_excepton
自定义中间件
# 创建自定义中间件
1.创建一个任意名称的文件夹
2.在该文件夹内创建一个任意名称的py文件
3.在该py文件内编写中间件类(这个类必须要继承MiddlewareMixin)
4.配置文件中注册
# 必须掌握的方法
1.process_request
2.process_response
# 需要了解的方法
1.process_view
2.process_template_response
3.process_exception
process_request
1.请求来的时候需要经过每一个中间件里面的process_request方法
结果的顺序是按照配置文件中注册的中间件从上往下的顺序依次执行
2.如果中间件里面没有定义该方法,那么直接跳过执行下一个中间件
3.如果该方法返回HttpResponse对象,那么请求不再继续往后直接返回相应的数据(校验失败不允许访问)
process_response
1.响应走的时候需要经过每一个中间件里面的process_response方法
'该方法有两个额外参数request,response'
2.该方法必须返回一个HttpResponse对象
	2.1默认返回的就是形参response
	2.2你也可以自己返回自己的
3.顺序是按照配置文件中注册了中间价从下往上依次执行经过所有的中间里面的process_response
如果你没有定义的话,直接跳过执行下一个
'''
如果第一个process_response方法已经返回了一个HttpResponse对象,那么响应会替换成该HttpResponse对象数据 而不再是视图函数想要返回给客户端的数据
'''
其他方法
# process_view
  	路由匹配成功之后执行视图之前从上往下执行配置文件中注册了的中间件里面的process_view方法
    
# process_template_response
  	视图函数执行完毕之后返回的对象中含有render属性对应一个render方法
    则会从下往上执行配置文件中注册了的中间件里面的process_template_response方法
    
# process_exception
  	视图函数执行过程中报错并在返回响应的时候会从下往上执行配置文件中注册了的中间件里面的process_exception

csrf跨站请求伪造
# csrf的简介
CSRF(Cross Site Request Forgery, 跨站域请求伪造)是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安全隐患之一。其他安全隐患,比如 SQL 脚本注入,跨站域脚本攻击等在近年来已经逐渐为众人熟知,很多网站也都针对他们进行了防御
'''
攻击者(黑客,钓鱼网站)盗用了你的身份,以你的名义发送恶意请求,这些请求包括发送邮件,发送消息,盗取账号,购买商品,银行转账,从而使你的个人隐私泄露和财产损失
'''

csrf简单模拟
# csrf模拟
钓鱼网站:一个模仿正规网站的网址 诱骗用户在该网站上做本应该在正规网站上做的操作,从而获取到该用户在正规网站上的数据甚至是财产
  eg:假设我们需要登录网页完成转账操作
    我们不小心登录到了钓鱼网站 填写了账户 密码 对方账户等信息
    点击转账之后我们账户的钱确实减少了 但是对方账户却变成了一个你从来不认识的人
  原理:将收款人的账号 提前写成犯罪分子的然后隐藏 暴露给用户一个没有name属性的标签写着玩
# 钓鱼网站 原理
1.我们在钓鱼网站的页面,针对对方账户,只给用户提供一个没有name属性的普通input框
2.然后我们在内部隐藏一个已经写好name和value和input框
# 正规网站
# html
<h1>我是正儿八经的网站</h1>
<form action="" method="post">
    <p>username: <input type="text" name="username"></p>
    <p>target_user: <input type="text" name="target_user"></p>
    <p>money: <input type="text" name="money"></p>
    <input type="submit">
</form>
# views
def transfer(request):
    if request.method == 'POST'
        username = request.POST.:get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        print('%s给%s转了%s元' % (username, target_user, money))
    return render(request, 'transfer.html')
# 不正规钓鱼网站	
<h1>我是钓鱼网站</h1>
<form action="http://127.0.0.1:8000/transfer/" method="post">
    <p>username: <input type="text" name="username"></p>
    <p>target_user: <input type="text"></p>
    {# 隐藏 #}
    <input type="text" name="target_user" value="jason" style="display: none">
    <p>money: <input type="text" name="money"></p>
    <input type="submit">
</form>
        
def transfer(request):
    return render(request, 'transfer.html')

csrf攻击原理
网站是通过cookie来实现登录功能的。而cookie只要存在浏览器中,那么浏览器在访问这个cookie的服务器的时候,就会自动的携带cookie信息到服务器上去。那么这时候就存在一个漏洞了,如果你访问了一个别有用心或病毒网站,这个网站可以在网页源代码中插入js代码,使用js代码给其他服务器发送请求(比如ICBC的转账请求)。那么因为在发送请求的时候,浏览器会自动的把cookie发送给对应的服务器,这时候相应的服务器(比如ICBC网站),就不知道这个请求是伪造的,就被欺骗过去了。从而达到在用户不知情的情况下,给某个服务器发送了一个请求
csrf解决策略
# form表单
	<form action="" method="post">
    {% csrf_token %} // 给正规的网站页面都添加一个唯一标识
    <p>当前账户:<input type="text" name="current_user"></p>
    <p>目标账户:<input type="text" name="target_user"></p>
    <p>转账金额:<input type="text" name="money"></p>
    <input type="submit">
	</form>

ajax校验
# 方式1:页面任意位置先写{% csrf_token %} 之后获取数据 
'csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()
# 方式2:模板语法直接获取
'csrfmiddlewaretoken':{{ csrf_token }}
<button id="d1">ajax请求</button>
{# 动态解析 静态文件#}
{% load static %}
<script src="{% static 'js/mysetup.js' %}"></script>
<script>
    $('#d1').click(function () {
        $.ajax({
            url:'',  // 不写默认提交地址当前
            type:'post',  // 请求
            // 第一种
            {#data:{"username":"jason", 'csrfmiddlewaretoken':$('[name=csrfmiddlewretoken]').val()},#}
            // 第二种
            {#data:{"username":'jason', 'csrfmiddlewaretoken':'{{ csrf_token }}'},#}
            data:{"username":'jason'},  // 数据
            success:function () {
            }
        })
    })
</script>
通用方式校验ajax
# 通用解决方案:js脚本自动处理
	也只能适用于ajax提交  form表单还是需要额外指定
    
将文件配置到你的Django项目的静态文件中,在html页面上通过导入该文件即可自动帮我们解决ajax提交post数据时校验csrf_token的问题,(导入该配置文件之前,需要先导入jQuery,因为这个配置文件内的内容是基于jQuery来实现的)
# 参考网址
https://docs.djangoproject.com/en/1.11/ref/csrf/
getCookie方法
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');
使用$.ajaxSetup()方法为ajax请求统一设置
function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
  beforeSend: function (xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
      xhr.setRequestHeader("X-CSRFToken", csrftoken);
    }
  }
});
