十二:Django框架学习之中间件 0

什么是中间件?

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

说的直白一些,中间件是帮助我们在视图函数执行之前执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法。

django框架中的七大中间件
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',
]
django框架请求生命周期流程图

img

img

自定义中间件

# 中间件可以定义五个方法,分别是:
(1)process_request(self,request)
(2)process_response(self,request,response)
(3)process_view(self, request, view_func, view_args, view_kwargs)
(4)process_template_response(self,request,response)
(5)process_exception(self, request, exception)

以上方法的返回值可以是None或者HttpResponse对象,如果是None,则按照django定义的规则继续向后执行;如果是HttpResponse对象,

自定义中间件步骤

  • 在项目名或者应用名下,创建一个任意名称的文件夹,如mymiddleware;
  • 在该文件夹下创建一个任意名称的 py文件,如auths.py;
  • 在该py文件内书写类,这个类必须继承MiddlewareMixin;
  • 需要将类的路径以字符串的形式注册到配置文件中才能生效
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    ...

    # 自定义的中间件
    'app01.mymiddleware.auths.Login',
    'app01.mymiddleware.auths.Register',
]

process_request(必须掌握)

"""
process_request有一个参数,就是request,这个request和视图函数中的request是一样的(在交给Django后面的路由之前,对这个request对象可以进行一系列的操作)。

由于request对象是一样的,所以我们可以对request对象进行一系列的操作,包括request.变量名=变量值,这样的操作,我们可以在后续的视图函数中通过相同的方式即可获取到我们在中间件中设置的值。
"""

auths.py

from django.utils.deprecation import MiddlewareMixin


class Login(MiddlewareMixin):
    def process_request(self, request):
        print('login里面的process_request!')


class Register(MiddlewareMixin):
    def process_request(self, request):
        print('register里面的process_request!')

views.py

def index(request):
    print('我是app01的index函数')
    return render(request,'index.html',{'msg':'你是我小宝贝'})

执行结果:

img

如果将process_request方法的返回值换HttpResponse对象

class Login(MiddlewareMixin):
    def process_request(self, request):
        return HttpResponse('i am ok!')


class Register(MiddlewareMixin):
    def process_request(self, request):
        return HttpResponse('i am ok!')

img

总结:

(1)中间件的process_request方法是在执行视图函数之前执行的;
(2)当配置多个中间件时,会按照MIDDLEWARE中的注册顺序,也就是列表的索引值,从前到后依次执行的;
(3)如果某个中间件中没有process_request方法,则跳过继续执行下一个中间件;
(4)如果process_request方法的返回值是HttpResponse对象,那么请求不再继续往下执行,而是原路返回(校验失败不允许访问);
(5)不同中间件之间传递的request都是同一个对象。

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

process_response(必须掌握)

"""
定义process_response方法时,必须给方法传入两个形参(request和response)。request就是上述例子中一样的对象,response是视图函数返回的HttpResponse对象(也就是说这是Django后台处理完之后给出一个的一个具体的视图)。该方法的返回值(必须要有返回值)也必须是HttpResponse对象。如果不返回response而返回其他对象,则浏览器不会拿到Django后台给他的视图,而是我的中间件中返回的对象。
"""

auths.py

class Login(MiddlewareMixin):
    def process_request(self, request):
        print('我是login函数的process_request!')

    def process_response(self, request, response):
        print('我是login函数的process_response!')
        return response


class Register(MiddlewareMixin):
    def process_request(self, request):
        print('我是register函数的process_request!')

    def process_response(self, request, response):
        print('我是register函数的process_request!')
        return response

执行结果:

img

总结:

(1)中间件的process_response方法是在执行视图函数之后执行的;
(2)当配置多个中间件时,会按照MIDDLEWARE中的注册顺序倒序执行,也就是说:第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后执行,而它的process_response方法首先执行;
(3)process_response方法,有两个参数(request,response),该方法必须返回一个HttpResponse对象,默认是返回的形参(response,你也可以返回自己定义的HttpResponse对象);
(4)如果某个中间件中没有process_response方法,则跳过继续执行下一个中间件;

问题:

"""
如果在第一个process_request方法就已经返回了HttpResponse对象,那么响应走的时候是经过所有的中间件里面的process_response还是有其他情况?
"""

答:会走同级别的process_response方法返回。

对比FlASK框架
"""
只要返回数据了就必须经过所有中间件里面的类似于process_reponse方法
"""

process_view(了解)

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

auths.py

class Login(MiddlewareMixin):
    def process_request(self, request):
        print('我是login函数的process_request!')

    def process_response(self, request, response):
        print('我是login函数的process_response!')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        """
        request是HttpRequest对象,
        view_func是Django即将使用的视图函数
        view_args是将传递给视图的位置参数的列表
        view_kwargs是将传递给视图的关键字参数的字典
        """
        print('=' * 60)
        print('Login函数中的process_view')
        print(view_func, view_func.__name__)


class Register(MiddlewareMixin):
    def process_request(self, request):
        print('我是register函数的process_request!')

    def process_response(self, request, response):
        print('我是register函数的process_response!')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('=' * 60)
        print('Register函数中的process_view')
        print(view_func, view_func.__name__)

img

总结:

(1)中间件的process_views方法是在执行视图函数之前执行的;
(2)该方法返回一个None或者HttpResponse对象。如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,那么将不会执行Django的视图函数,而是直接在中间件中掉头,倒叙执行一个个process_response方法,最后返回给浏览器

process_exception(了解)

"""
当视图函数中出现异常的情况下触发
顺序是按照配置文件中注册了的中间件从下往上依次经过
"""

process_exception(self,request,exception)
request:一个HttpRequest对象
exception:视图函数异常产生的Exception对象

auths.py

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class Login(MiddlewareMixin):
    def process_request(self, request):
        print('我是login函数的process_request!')

    def process_response(self, request, response):
        print('我是login函数的process_response!')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        """
        request是HttpRequest对象,
        view_func是Django即将使用的视图函数
        view_args是将传递给视图的位置参数的列表
        view_kwargs是将传递给视图的关键字参数的字典
        """
        print('=' * 60)
        print('Login函数中的process_view')
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print('Login函数中process_exception')


class Register(MiddlewareMixin):
    def process_request(self, request):
        print('我是register函数的process_request!')

    def process_response(self, request, response):
        print('我是register函数的process_response!')
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print('=' * 60)
        print('Register函数中的process_view')
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print('Register函数中process_exception')

views.py

from django.shortcuts import render
from django.shortcuts import HttpResponse


# Create your views here.

def index(request):
    print('我是app01的index函数')
    raise ValueError('天王盖地虎')
    return render(request,'index.html',{'msg':'你是我小宝贝'})

img

总结:

"""
这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。
"""

如果将Register中的process_exception的返回值换成HttpResponse对象

    def process_exception(self, request, exception):
        print(exception)
        print('Register函数中process_exception')
        return HttpResponse(f'<a href="http://www.mzitu.com">{exception}</a>')

img

process_template_response(用的比较少)

"""
返回的HttpResponse对象有render属性的时候才会触发
顺序是按照配置文件中注册了的中间件从下往上依次经过
"""

中间件的执行流程

"""
请求到达中间件之后,先按照正序执行每个注册中间件的process_request方法,process_request方法返回的值是None,就依次执行,如果返回的值是HttpResponse对象,不再执行后面的process_request方法,而是执行当前对应中间件的process_response方法(注意不是掉头执行所有的process_response方法),将HttpResponse对象返回给浏览器。也就是说:如果MIDDLEWARE中注册了6个中间件,执行过程中,第3个中间件返回了一个HttpResponse对象,那么第4,5,6中间件的process_request和process_response方法都不执行,顺序执行3,2,1中间件的process_response方法。
"""

img

"""
process_request方法都执行完后,匹配路由,找到要执行的视图函数,先不执行视图函数,先执行中间件中的process_view方法,process_view方法返回None,继续按顺序执行,所有process_view方法执行完后执行视图函数。假如中间件3 的process_view方法返回了HttpResponse对象,则4,5,6的process_view以及视图函数都不执行,直接从最后一个中间件,也就是中间件6的process_response方法开始倒序执行。
"""

img

"""
process_template_response和process_exception两个方法的触发是有条件的,执行顺序也是倒序。总结所有的执行流程如下:
"""

img

img

crsf跨站请求伪造

"""
钓鱼网站
	我搭建一个跟正规网站一模一样的界面(中国银行)
	用户不小心进入到了我们的网站,用户给某个人打钱
	打钱的操作确确实实是提交给了中国银行的系统,用户的钱也确确实实减少了
	但是唯一不同的时候打钱的账户不适用户想要打的账户变成了一个莫名其妙的账户
    
内部本质
	我们在钓鱼网站的页面 针对对方账户 只给用户提供一个没有name属性的普通input框
	然后我们在内部隐藏一个已经写好name和value的input框
"""

如何规避上述问题
	csrf跨站请求伪造校验
		网站在给用户返回一个具有提交数据功能页面的时候会给这个页面加一个唯一标识
		当这个页面朝后端发送post请求的时候 我的后端会先校验唯一标识,如果唯一标识不对直接拒绝(403 forbbiden)如果成功则正常执行	

form表单csrf跨站请求伪造校验

views.py

def index(request):
    form_obj = forms.User()
    if request.method == 'POST':
        username = request.POST.get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        return HttpResponse(f'{username}给{target_user}转账{money}元!')
    return render(request, 'index.html', {'form_obj': form_obj})

index.html

            <form action="{% url 'index' %}" method="post" novalidate enctype="multipart/form-data">
                {% csrf_token %}
                <table class="table table-striped table-hover table-bordered">
                    <tbody>
                    {% for form in form_obj %}
                        <tr>
                            <td class="text-center">{{ form.label }}</td>
                            <td>{{ form }}
                                <span style="color: red">{{ form.errors.0 }}</span>
                            </td>
                        </tr>
                    {% endfor %}
                    </tbody>
                </table>
                <input type="submit" value="提交" class="btn btn-success btn-sm form-control">
            </form>

img

img

如果没有加{% crsf_token %},则会报403 forbbiden错误

ajax csrf跨站请求伪造校验

<script>
    let $btnEle = $('#d1');
    $btnEle.on('click', function () {
        $.ajax({
            url: '/index/',
            type: 'post',
            data: {'username': 'jason_dsb', 'password': 'dbj666'},
            success: function (args) {
                
            }
        });
    })
</script>

img

  • 第一种方式(利用标签查找获取页面上的随机字符串)
data: {
    'username': 'jason_dsb',
    'password': 'dbj666',
    'csrfmiddlewaretoken': $('[name=csrfmiddlewaretoken]').val(),
},
  • 第二种方式(利用模版语法提供的快捷书写)
data: {
    'username': 'jason_dsb',
    'password': 'dbj666',
    'csrfmiddlewaretoken': '{{ csrf_token }}',
},
  • 第三种方式(通用方式,直接拷贝js代码并应用到自己的html页面上即可)

(1)在static文件夹下的js文件中新建一个mysetup.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);
    }
  }
});

(2)在html页面导入它

<script src="{% static 'js/mysetup.js' %}"></script>

csrf相关装饰器

需求

"""
1.网站整体都不校验csrf,就单单几个视图函数需要校验
2.网站整体都校验csrf,就单单几个视图函数不校验
"""

FBV视图函数

# 网站整体都校验csrf,就单单几个视图函数不校验
from django.views.decorators.csrf import csrf_protect, csrf_exempt

@csrf_exempt
def index(request):
    form_obj = forms.User()
    if request.method == 'POST':
        username = request.POST.get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        return HttpResponse(f'{username}给{target_user}转账{money}元!')
    return render(request, 'index.html', {'form_obj': form_obj})

# 网站整体都不校验csrf,就单单几个视图函数需要校验
# 'django.middleware.csrf.CsrfViewMiddleware',
@csrf_protect
def index(request):
    form_obj = forms.User()
    if request.method == 'POST':
        username = request.POST.get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        return HttpResponse(f'{username}给{target_user}转账{money}元!')
    return render(request, 'index.html', {'form_obj': form_obj})

CBV视图函数

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

# @method_decorator(csrf_exempt,name='dispatch') # 针对csrf_exempt 第三种方式可以
# @method_decorator(csrf_protect,name='post')  # 针对csrf_protect 第二种方式可以
# @method_decorator(csrf_exempt,name='post')  # 针对csrf_exempt 第二种方式不可以
class MyLogin(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):
        form_obj = forms.User()
        return render(request, 'index.html', {'form_obj': form_obj})

    # @method_decorator(csrf_protect)   # 针对csrf_protect 第一种方式可以
    # @method_decorator(csrf_exempt)  # 针对csrf_exempt 第一种方式不可以
    def post(self, request):
        username = request.POST.get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        return HttpResponse(f'{username}给{target_user}转账{money}元!')
posted @ 2020-11-08 17:39  为了等  阅读(173)  评论(0编辑  收藏  举报