目录
第七章、中间件续写
一、中间件的执行顺序
测试思路:
- 在 settings.py 里注册不同中间件,探究默认的执行顺序
- 在不同中间件的 process_request 和 process_response 等方法中 return HttpResponse 对象会对执行顺序造成什么影响
- 了解五种方法的触发时机
自定义中间件
- 新建一个文件夹(放在全局或 app 内)
- 写一个类继承 MiddlewareMiXin 类
- 里面书写需要的(五个方法中的某些)方法
- 一定要在 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
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')
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')
ensure_csrf_cookie
这个是我的知识盲区 后续再补
四、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
在这里感谢!