Django 中间件与csrf跨站请求

文章目录

  • Django 中间件与csrf跨站请求
    • Django中间件
    • 自定义中间件
    • csrf中间件介绍
      • csrf校验使用
      • csrf相关装饰器
    • importlib
    • 基于Django中间件的重要编程思想

Django 中间件与csrf跨站请求

Django中间件

按照官方说法:Django中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。

简单点说Django中间件就是Django的门户,主要功能有两点

  1. 请求来的时候需要先经过中间件才能达到真正的Django后端
  2. 响应走的时候最后也需要经过中间件才能发送出去

Django自带有七个中间件,在settings.py文件中,有个 MIDDLEWARE 配置项

Django 中间件与csrf跨站请求_第1张图片

注意: 由于中间件影响的是全局,所以需要谨慎使用

补充: MIDDLEWARE 配置项是一个列表,列表中是一个个字符串,这些字符串就是一个个类,也是一个个中间件,中间件执行的顺序请求时从上往下,响应时从下往上,可以参考Django生命周期流程图

Django 中间件与csrf跨站请求_第2张图片
Django 中间件与csrf跨站请求_第3张图片

自定义中间件

  • Django支持程序员自定义中间件,并且暴露给程序员五个可以自定义的方法
    1. process_request
    2. process_response
    3. process_view
    4. process_template_response
    5. process_exception

自定义中间件的方法:

  1. 在项目名或者应用名下创建一个任意名称的文件夹

  2. 在该文件夹内创建一个任意名称的py文件

  3. 在该py文件内需要书写类(这个类必须继承MiddlewareMixin)

    然后在这个类里面就可以定义五个方法了(五个方法用几个写几个即可,不用全写)

  4. 需要将类的路径以字符串的形式注册到配置文件中(这里有涉及到了另一个编程思想,后文会详细介绍)

自定义中间件5种方法特性详细介绍

  1. 重点掌握(process_request和process_response)

    process_request:

    1. 当请求来的时候需要经过每一个中间件里面的process_request方法,顺序按照配置文件中注册的中间件从上往下的顺序依次执行

    2. 如果中间件里面没有定义该方法,那么直接跳过执行下一个中间件

    3. 如果该方法返回了HttpResponse对象,那么请求将不再继续往后面执行,而是直接原路返回

      process_request 方法就是用来做全局相关的所有限制功能

    process_response:

    1. 响应走的时候需要经过每一个中间件里面的process_response方法,该方法有两个额外的参数request,response
    2. 该方法必须返回一个HttpResponse对象
      1. 默认返回的就是形参response
      2. 你也可以自己返回自己的,不过那也做就没意义了
    3. 顺序是按照配置文件中注册了的中间件从下往上依次经过,同理如果没有定义就跳过执行下一个

    补充:

    • 如果在中间件中process_request方法就返回了一个HttpResponse对象,那么响应走的时候走回直接走同级的process_response返回
    • 这一特性同flask框架中的中间件不一样,在flask中只要返回了数据就必须经过所有中间件的类似于process_response方法

Django 中间件与csrf跨站请求_第4张图片

  1. 了解即可

    process_view:

    1. 在路由匹配成功之后执行视图函数之前,会自动执行中间件里面的该方法
    2. 顺序按照配置文件中注册的中间件从上往下的顺序依次执行

    process_template_response:

    1. 返回的HttpResponse对象有render属性的时候才会触发
    2. 顺序按照配置文件中注册的中间件从下往上的顺序依次执行

    process_exception

    1. 当视图函数出现异常的情况下触发
    2. 顺序按照配置文件中注册的中间件从下往上的顺序依次执行

Django 中间件与csrf跨站请求_第5张图片
Django 中间件与csrf跨站请求_第6张图片

csrf中间件介绍

在我们刚接触Django,需要提交post请求的是时候,通常需要我们将MIDDLEWARE中的csrf中间件注销 ,这是因为在我们发送post请求的时候,不做任何处理,csrf中间件是无法校验通过的。

其主要作用是用来防止钓鱼网站

csrf原理: 在用户提交get请求获取数据时,服务器会随机生成一个唯一标识(随机字符串,每一次都不一样)返回给网站,当用户提交post请求的时候,会把将这个唯一标识携带,当经过csrf中间件的时候,会进行校验,校验成功,才会允许post请求中的数据通过。

csrf校验使用

  1. form表单校验
<form action="" method="post">
    {% csrf_token %}
    <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>

在这里插入图片描述

  1. ajax校验
1. 利用标签查找获取页面上的唯一标识
data:{"username":'xxx','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},
    
2. 利用模板语法提供的快捷书写
data:{"username":'xxx','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},
    
3. 通用方式,直接拷贝js代码(官方提供)并应用到自己的html页面上即可 
  • 官方源码
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);
    }
  }
});

csrf相关装饰器

from django.views.decorators.csrf import csrf_protect,csrf_exempt
from django.utils.decorators import method_decorator

'''
csrf_protect  需要校验
    针对csrf_protect符合我们之前所学的装饰器的三种玩法
    
csrf_exempt   忽视校验
    针对csrf_exempt只能给dispatch方法加才有效    
'''
1. 针对FBV: 正常使用即可
    # @csrf_exempt
	@csrf_protect
    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')

2. 针对CBV
    from django.views import View

    # @method_decorator(csrf_protect,name='post')  # 针对csrf_protect 第二种方式可以
    # @method_decorator(csrf_exempt,name='post')  # 针对csrf_exempt 第二种方式不可以
    @method_decorator(csrf_exempt,name='dispatch')
    class MyCsrfToken(View):
        # @method_decorator(csrf_protect)  # 针对csrf_protect 第三种方式可以
        # @method_decorator(csrf_exempt)  # 针对csrf_exempt 第三种方式可以
        def dispatch(self, request, *args, **kwargs):
            return super(MyCsrfToken, self).dispatch(request,*args,**kwargs)

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

        # @method_decorator(csrf_protect)  # 针对csrf_protect 第一种方式可以
        # @method_decorator(csrf_exempt)  # 针对csrf_exempt 第一种方式不可以
        def post(self,request):
            return HttpResponse('post')


importlib

importlib模块: 能够通过字符串来导入模块,这也是上述中间件为什么能够通过字符串的方式导入模块的关键所在

import importlib
res = 'myfile.b'
ret = importlib.import_module(res)  # from myfile import b
# 该方法最小只能到py文件名
print(ret)   # 结果是一个对象

基于Django中间件的重要编程思想

  • 简单点说就是在写功能时,学Django中间件的用法,使用时只需要在配置文件中,注释掉对应配置信息,就能够不使用对应的功能。

核心代码:

import settings
import importlib


def send_all(content):
    # 获取配置文件中的模块路径和类名
    for path_str in settings.NOTIFY_LIST:  # 'notify.email.Email'
        module_path,class_name = path_str.rsplit('.',maxsplit=1)
        # module_path = 'notify.email'  class_name = 'Email'
        # 1 利用字符串导入模块
        module = importlib.import_module(module_path)  # from notify import email
        # 2 利用反射获取类名
        cls = getattr(module,class_name)  # Email、QQ、Wechat
        # 3 生成类的对象
        obj = cls()
        # 4 利用鸭子类型直接调用send方法
        obj.send(content)

你可能感兴趣的:(#,Django框架,django,中间件,python)