第七章、中间件续写

第七章、中间件续写

一、中间件的执行顺序

测试思路:

  • 在 settings.py 里注册不同中间件,探究默认的执行顺序
  • 在不同中间件的 process_request 和 process_response 等方法中 return HttpResponse 对象会对执行顺序造成什么影响
  • 了解五种方法的触发时机

自定义中间件

  1. 新建一个文件夹(放在全局或 app 内)
  2. 写一个类继承 MiddlewareMiXin 类
  3. 里面书写需要的(五个方法中的某些)方法
  4. 一定要在 settings.py 里配置中间件注册

代码

mymiddleware/mdd.py 自定义中间件

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class MyMdd(MiddlewareMixin):
def process_request(self, request):
print('我是第一个中间件里面的process_request方法')
def process_response(self, request, response):
print('我是第一个中间件里面的process_response方法')
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print(view_func)
print(view_args)
print(view_kwargs)
print('我是第一个中间件里面的process_view方法')
def process_exception(self, request, exception):
print('我是第一个中间件里面的process_exception')
def process_template_response(self, request, response):
print('我是第一个中间件里面的process_template_response')
return response
class MyMdd1(MiddlewareMixin):
def process_request(self, request):
print('我是第二个中间件里面的process_request方法')
def process_response(self, request, response):
print('我是第二个中间件里面的process_response方法')
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print(view_func)
print(view_args)
print(view_kwargs)
print('我是第二个中间件里面的process_view方法')
def process_exception(self, request, exception):
print('我是第二个中间件里面的process_exception')
def process_template_response(self, request, response):
print('我是第二个中间件里面的process_template_response')
return response
class MyMdd2(MiddlewareMixin):
def process_request(self, request):
print('我是第三个中间件里面的process_request方法')
def process_response(self, request, response):
print('我是第三个中间件里面的process_response方法')
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print(view_func)
print(view_args)
print(view_kwargs)
print('我是第三个中间件里面的process_view方法')
def process_exception(self, request, exception):
print('我是第三个中间件里面的process_exception')
def process_template_response(self, request, response):
print('我是第三个中间件里面的process_template_response')
return response

在 settings.py 中的配置

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',
'mymiddleware.mdd.MyMdd', # 配置上
'mymiddleware.mdd.MyMdd1', # 配置上
'mymiddleware.mdd.MyMdd2', # 配置上
]

我们可以总结一下:

process_request

请求来的时候会依次执行 settings.py 配置文件中注册了的中间件里的该方法

  • 如果没有该方法则直接跳过,走下一个中间件
  • 如果该方法里返回了 HttpResponse 对象,那么会直接从当前中间件的 process_response 方法 从下往上依次执行返回,不会再接着往下执行
  • 执行顺序:从上往下
  • 该方法可以实现对用户身份的校验,访问频率的限制,用户权限的校验...

process_response

响应走的时候会依次执行 settings.py 配置文件中注册了的中间件里的该方法(必须将 response 形参返回,因为这个 response 指代的就是返回给前端的数据)

  • 如果没有该方法则直接跳过,走下一个中间件
  • 执行顺序:从下往上
  • 该方法可以帮你实现缓存机制(减缓服务器、数据库的压力)

process_view

路由匹配成功 执行视图函数之前 自动触发(从上往下依次执行)

process_exception

视图函数报错了,自动触发(从下往上依次执行)

process_template_response

视图函数返回的 HttpResponse 对象中包含了 render 属性时会触发,或者是表明一个对象时 TemplateResponse 对象或等价方法 的时候也会触发(从下往上依次执行)

def index(request):
    print("我是 index 视图函数")
    def render():
        return HttpRespone('用户最终能够看到的结果')  # ******
    obj = HttpResponse('index')
    obj.render = render  # 返回的 HttpResponse 对象中必须包含 render 属性,才能触发中间件里定义的 process_template_response 方法
    return obj

强调:在写中间件的时候,只要形参中有 response ,就要记得将其返回,这个Response 是要给前端的信息

二、跨站请求伪造

顾名思义:可以做出钓鱼网站这种的
就类似于你搭建了一个跟银行一模一样的web页面
用户在你的网站转账的时候输入用户名 密码 对方账户
银行里面的钱确实少了 但是发现收款人变了

到底咋回事呢?

你写的form表单中 用户的用户名  密码都会真实的提交给银行后台
但是收款人的账户却不是用户填的 你暴露给用户的是一个没有name属性的input框
你自己提前写好了一个隐藏的带有name和value的input框

解决钓鱼网站的策略


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

三、csrf装饰器

from django.views.decorators.csrf import csrf_exempt,csrf_protect,ensure_csrf_cookie

csrf_exempt   # 某个视图不需要进行csrf校验

csrf_protect  # 某个视图需要进行csrf校验

ensure_csrf_cookie  # 确保生成csrf的cookie
  1. csrf_exempt

    只有两种装饰的方式:

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

    除了csrf_exempt之外,所有的其他装饰器在CBV上面都有三种方式:

    @method_decorator(csrf_protect,name='post')
    class MyCsrf(View):
        @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')
    
  3. 这个是我的知识盲区 后续再补

四、post请求提交数据通过 csrf 校验

目前所学的拥有post请求的方法有form表单和ajax发送

form表单

你在写的时候只需要加上一个
            {% csrf_token %}

ajax发送有三种

第一种  自己再页面上先通过{% csrf_token %}获取到随机字符串  然后利用标签查找 
data:{'username':'jason','csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()},
第二种
data:{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'},
第三种
sataic文件夹 再创建一个js文件 拷贝代码

下面给出第三种的js代码

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');



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);
    }
  }
});

然后在html文件上加这一行就好了

{#<script src="/static/setup.js"></script>#}

这样就不需要 在 html 中写 {% csrf_token %} 或在 ajax 中写 {{ csrf_token }}

五、自我拷问

  • 当你网站全局都需要校验 csrf 的时候(未注释掉 csrf 中间件时),有几个不需要校验该如何处理? @csrf_exempt

  • 当你的网站全局不校验 csrf 的时候(注释掉 csrf 中间件时),有几个需要校验该如何处理 ?@csrf_protect

分FBV和CBV

未注释掉 csrf 中间件时 单功能取消 csrf 校验:csrf_exempt

FBV怎么写?

from django.views.decorators.csrf import csrf_exempt

# 全局开启时,局部禁用
@csrf_exempt
def index(request):
  pass

CBV怎么写?

有两种方式,不能针对单个方法,是针对全局的

# CBV比较特殊,不能单独加在某个方法上
# 只能加在类上或dispatch方法上
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt


# @method_decorator(csrf_exempt,name='dispatch')  # 第一种
class Csrf_Token(View):
    @method_decorator(csrf_exempt)  # 第二种
    def dispatch(self,request,*args,**kwargs):
        res = super().dispatch(request,*args,**kwargs)
        return res
      # @method_decorator(csrf_exempt)  # 这里这么写不行!!!
    def get(self,request):
            pass
    def post(self,request):
            pass

注释掉 csrf 中间件时 单功能开启 csrf 校验:csrf_protect

FBV怎么写?

from django.views.decorators.csrf import csrf_protect


@csrf_protect
def lll(request):
    return HttpResponse('lll')

CBV怎么写?

from django.views.decorators.csrf import csrf_protect

from django.views import View
from django.utils.decorators import method_decorator


# 第一种方式
# @method_decorator(csrf_protect,name='post')  # 有效的
class MyView(View):
    @method_decorator(csrf_protect)  # 第三种方式
    def dispatch(self, request, *args, **kwargs):
        res = super().dispatch(request, *args, **kwargs)
        return res

    def get(self, request):
        return HttpResponse('get')

    # 第二种方式
    # @method_decorator(csrf_protect)  # 有效的
    def post(self, request):
        return HttpResponse('post')

原文借鉴了https://www.cnblogs.com/suwanbin/p/11591887.html

在这里感谢!

原文地址:https://www.cnblogs.com/demiao/p/11768709.html