中间件组件与csrf_token
Django中间件
官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。
但是由于其影响的是全局,所以需要谨慎使用,使用不当会影响性能。
说的直白一点中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法。
我们一直都在使用中间件,只是没有注意到而已,打开Django项目的Settings.py文件,看到下图的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',
]
MIDDLEWARE配置项是一个列表(列表是有序的,记住这一点,后面你就知道为什么要强调有序二字),列表中是一个个字符串,这些字符串其实是一个个类,也就是一个个中间件。
自定义中间件
中间件可以定义五个方法,分别是:(主要的是process_request和process_response)
process_request(self,request)
process_view(self, request, view_func, view_args, view_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)
以上方法的返回值可以是None或一个HttpResponse对象,如果是None,则继续按照django定义的规则向后继续执行,如果是HttpResponse对象,则直接将该对象返回给用户。
自定义中间件示例
-
新建一个任意名字的文件夹,里面新建一个任意名字的py文件
-
from django.utils.deprecation import MiddlewareMixin


process_request
process_request只有一个request参数。
这个request参数和视图函数中的request是一样的(在交给Django路由之前,可以对这个对象进行一系列操作)
它的返回值可以是None也可以是HttpResponse对象。返回值是None的话,按正常流程继续走,交给下一个中间件处理,如果是HttpResponse对象,Django将不执行视图函数,而将相应对象返回给浏览器。
-
中间件的process_request方法是在执行视图函数之前执行的
-
当配置多个中间件时,会按照MIDDLEWARE中的注册顺序,从上到下依次执行
-
不同中间件之间传递的request都是同一个对象
process_response
多个中间件的process_response方法是按照MIDDLEWARE中的顺序倒序执行的
(第一个中间件的process_request()方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。)
定义process_response方法时,必须给方法传入两个形参,request和response。
request与process_request中的是一样的对象,response是视图函数返回的HttpResponse对象,该方法必须有返回值且返回值必须是HttpResponse对象。如果不返回response而返回其他对象,那么浏览器不会拿到Django后台给他的视图,而是中间件返回的对象
class MD1(MiddlewareMixin):
def process_response(self, request, response):
print("MD1里面的 process_response")
return response # 必须返回
process_view
process_view(self,request,view_func,view_args,view_kwargs)
-
该方法有四个参数:
- request是HttpResponse对象
- view_func是Django即将使用的视图函数(它是实际的函数对象,而不是函数的名称作为字符串.)
- view_args是将传递给视图的位置参数的列表.
- view_kwargs是将传递给视图的关键字参数的字典。 view_args和view_kwargs都不包含第一个视图参数(request)。
-
Django会在调用视图函数之前调用process_view方法
-
该方法返回None或者一个Httpresponse对象, 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,那么将不会执行Django的视图函数,而是直接在中间件中掉头,倒叙执行一个个process_response方法,最后返回给浏览器
-
process_view方法是在Django路由系统之后,视图系统之前执行的,执行顺序按照MIDDLEWARE中的注册顺序从前到后顺序执行的
process_exception
process_exception(self, request, exception)
-
该方法两个参数:
-
一个HttpRequest对象
-
一个exception是视图函数异常产生的Exception对象。
-
-
这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。
process_template_response(用的比较少)
process_template_response(self, request, response)
-
它的参数,一个HttpRequest对象,response是TemplateResponse对象(由视图函数或者中间件产生)。
-
process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)。
def index(request): def render(): return HttpResponse("O98K") rep = HttpResponse("OK") rep.render = render return rep -
视图函数执行完之后,立即执行了中间件的process_template_response方法,顺序是倒序,先执行MD1的,在执行MD2的,接着执行了视图函数返回的HttpResponse对象的render方法,返回了一个新的HttpResponse对象,接着执行中间件的process_response方法。
中间件的执行流程
-
请求到达中间件之后,先按照正序执行每个注册中间件的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方法。

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

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

Django请求流程图

中间件之csrf跨站请求伪造
CSRF :表示django全局发送post请求均需要字符串验证
功能:防止跨站请求伪造的功能
工作原理:客户端访问服务器端,在服务器端正常返回给客户端数据的时候,而外返回给客户端一段字符串,等到客户端下次访问服务器端时,服务器端会到客户端查找先前返回的字符串,如果找到则继续,找不到就拒绝。
访问流程:客户端 >>> URL路由系统 >>> CSRF >>> 视图函数
需要在客户端页面的post表单内添加:
全局生效:
中间件 django.middleware.csrf.CsrfViewMiddleware
局部生效:
@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
form表单如何跨站请求伪造
原网站
<h1>正经的网站</h1>
<form action="/index3/" method="post">
<p>username:<input type="text" name="username"></p>
<p>money:<input type="text" name="money"></p>
<p>others:<input type="text" name="others"></p>
<input type="submit">
</form>
钓鱼网站
<h1>钓鱼网站</h1>
<form action="http://127.0.0.1:8000/transfer/" method="post">
<p>username:<input type="text" name="username"></p>
<p>money:<input type="text" name="money"></p>
<p>others:<input type="text"></p>
<input type="text" name="others" value="jason" style="display:none">
<input type="submit">
</form>
要执行CSRF中间件需要在前端加入{% csrf_token %}
<h1>正经的网站</h1>
<form action="/index3/" method="post">
{% csrf_token %}
<p>username:<input type="text" name="username"></p>
<p>money:<input type="text" name="money"></p>
<p>others:<input type="text" name="others"></p>
<input type="submit">
</form>
<input type="hidden" name="csrfmiddlewaretoken" value="2vzoo1lmSWgLTdI2rBQ4PTptJQKRQTRwnqeWRGcpdAQGagRp8yKg8RX2PdkF4aqh">
csrf装饰FBV
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt # 不校验csrf
def index1(request):
return HttpResponse('ok')
@csrf_protect # 校验csrf
def index2(request):
return HttpResponse('ok')
csrf装饰CBV
需要注意:
- csrf_protect 装饰时跟正常的CBV装饰器一样 有三种方法
- csrf_exempt 只能有下面两种方式
@method_decorator(csrf_exempt,name='dispatch') # 第一种
class Index3(View):
# @method_decorator(csrf_exempt) # 第二种
def dispatch(self, request, *args, **kwargs):
super().dispatch(request,*args,**kwargs)
# 其实都是给dispatch加
ajax进行csrf校验
<button>ajax</button>
<script>
$('button').click(function () {
$.ajax({
url:'',
type:'post',
data:{'name':'jason','csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()},
// 固定用法,找到{% csrf_token %}生成的标签,取到字符串
success:function (data) {
console.log(data)
}
})
})

浙公网安备 33010602011771号