1、中间件
1.定义
中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,而且在全局上改变django的输入与输出。由于改变的是全局,因此须要谨慎实用,用很差会影响到性能。javascript
每次请求到视图以前,或者响应到浏览器以前都会通过中间件的筛选html
2.基本使用
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内部已经封装好的中间件
3.自定义中间件
#中间件的方法 process_request(self,request) process_view(self, request, callback, callback_args, callback_kwargs) process_template_response(self,request,response) process_exception(self, request, exception) process_response(self, request, response)
#第一步:建立一个PY文件 #第二步:导入from django.utils.deprecation import MiddlewareMixin from django.utils.deprecation import MiddlewareMixin #第三步:新建自定义中间件process_request和process_response是必须的 class Mymiddle1(MiddlewareMixin): def process_request(self, request): print('Mymiddle1 request') def process_response(self, request, response): print('Mymiddle1 response') return response#process_response必定要返回HttpResponse对象 class Mymiddle2(MiddlewareMixin): def process_request(self, request): print('Mymiddle2 request') def process_response(self, request, response): print('Mymiddle2 response') return response
#第四步:写一个视图函数 from django.shortcuts import render,HttpResponse def index(requset): print('index') return HttpResponse('ok')
#第五步:在setting里设置 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', 'app01.mymiddleware.Mymiddle1', 'app01.mymiddleware.Mymiddle2' ] #请求是从上而下一层层的经过中间件的,以后才是执行视图层,而响应是从下而上一层层的返回去。
#结果 Mymiddle1 request Mymiddle2 request index Mymiddle2 response Mymiddle1 response
结论java
若是请求再经过中间件时,在process_request遇到了return返回,那么这个请求就不会再往下走进入视图了,而是从当前的process_response往上的返回python
from django.shortcuts import render,HttpResponse class Mymiddle1(MiddlewareMixin): def process_request(self, request): print('Mymiddle1 request') print('Mymiddle1 请求中断') return HttpResponse('Mymiddle1 请求中断') def process_response(self, request, response): print('Mymiddle1 response') return response#process_response必定要返回HttpResponse对象
#结果 Mymiddle1 request Mymiddle1 请求中断 Mymiddle1 response
4.process_view
#语法 process_view(self, request, #request是HttpRequest对象。 callback, #view_func是Django即将使用的视图函数。 callback_args, #view_args是将传递给视图的位置参数的列表. callback_kwargs #view_kwargs是将传递给视图的关键字参数的字典。 )
它应该返回None或一个HttpResponse对象。 若是返回None,Django将继续处理这个请求,执行任何其余中间件的process_view方法,而后在执行相应的视图。 若是它返回一个HttpResponse对象,Django不会调用适当的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。jquery
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import render,HttpResponse class Mymiddle1(MiddlewareMixin): def process_request(self, request): print('Mymiddle1 request') def process_response(self, request, response): print('Mymiddle1 response') return response def process_view(self, request, callback, callback_args, callback_kwargs): print("Mymiddle1 view") class Mymiddle2(MiddlewareMixin): def process_request(self, request): print('Mymiddle2 request') def process_response(self, request, response): print('Mymiddle2 response') return response def process_view(self, request, callback, callback_args, callback_kwargs): print("Mymiddle2 view")
Mymiddle1 request Mymiddle2 request Mymiddle1 view Mymiddle2 view index Mymiddle2 response Mymiddle1 response
结论ajax
若是中间件1中process_view在return,那么就会跳过以后的process_view和视图函数,直接执行process_responsedjango
def process_view(self, request, callback, callback_args, callback_kwargs): print('Mymiddle1 view中断') return HttpResponse('Mymiddle1 view中断')
#结果 Mymiddle1 request Mymiddle2 request Mymiddle1 view中断 Mymiddle2 response Mymiddle1 response
能够在process_view中调用视图函数,也会跳过以后的process_view和视图函数,直接执行process_response浏览器
def process_view(self, request, callback, callback_args, callback_kwargs): res = callback(request) print('Mymiddle1 view中断') return res
#结果 Mymiddle1 request Mymiddle2 request index Mymiddle1 view中断 Mymiddle2 response Mymiddle1 response
5.process_exception
process_exception(self, request, #HttpRequest对象 exception #视图函数异常产生的Exception对象 )
这个方法只有在视图函数中出现异常了才执行,它返回的值能够是一个None也能够是一个HttpResponse对象。若是是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,不然将默认处理异常。若是返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。服务器
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import render,HttpResponse class Mymiddle1(MiddlewareMixin): def process_request(self, request): print('Mymiddle1 request') # print('Mymiddle1 请求中断') # return HttpResponse('Mymiddle1 请求中断') def process_response(self, request, response): print('Mymiddle1 response') return response def process_view(self, request, callback, callback_args, callback_kwargs): print("Mymiddle1 view") # res = callback(request) # print('Mymiddle1 view中断') # return res def process_exception(self,request,exception): print("Mymiddle1 exception") class Mymiddle2(MiddlewareMixin): def process_request(self, request): print('Mymiddle2 request') def process_response(self, request, response): print('Mymiddle2 response') return response def process_view(self, request, callback, callback_args, callback_kwargs): print("Mymiddle2 view") def process_exception(self,request,exception): print("Mymiddle2 exception")
#结果 #当视图函数没有出现异常时,触发process_exception方法 Mymiddle1 request Mymiddle2 request Mymiddle1 view Mymiddle2 view index Mymiddle2 response Mymiddle1 response
结论cookie
process_exception是在视图函数执行完毕以后出发的,和process_response同样是从下往上的执行
若是此时视图函数出现了异常
def index(requset): print('index') asd#未定义的变量 return HttpResponse('ok')
1.自定义的process_exception中没有return,那么会内置的来抛出异常
Mymiddle1 request Mymiddle2 request Mymiddle1 view Mymiddle2 view index Mymiddle2 exception Mymiddle1 exception ... 异常信息 ... Mymiddle2 response Mymiddle1 response
2.自定义的process_exception中写了return,那么就不会在执行以后的process_exception,而是执行process_response
def process_exception(self,request,exception): print('Mymiddle2 抛出异常') return HttpResponse('Mymiddle2 抛出异常')
#结果 Mymiddle1 request Mymiddle2 request Mymiddle1 view Mymiddle2 view index Mymiddle2 抛出异常 Mymiddle2 response Mymiddle1 response
若是Mymiddle2的 process_exception没有返回HttpResponse对象,那么就会由从下往上的顺序,去找有没有返回HttpResponse对象的process_exception,直到找到。
#结果 Mymiddle1 request Mymiddle2 request Mymiddle1 view Mymiddle2 view index Mymiddle2 exception Mymiddle1 抛出异常 Mymiddle2 response Mymiddle1 response
6.中间件的实际应用
# 登录装饰器 lis = ['/shopping/', '/order/'] class Login(MiddlewareMixin): def process_request(self, request): url = request.get_full_path() if request.path in lis: if not request.session.get('is_login'): return redirect('/login/?next=%s' % url) def process_response(self, request, response): return response # 限制请求次数 import time class Overtimr(MiddlewareMixin): def process_request(self, request): IP = request.META['REMOTE_ADDR'] id_time = request.session.get(IP, []) t = time.time() if len(id_time) < 3: id_time.append(t) request.session[IP] = id_time else: if time.time() - id_time[0] < 60: print(time.time() - id_time[0]) return HttpResponse('操做频繁') else: print(time.time() - id_time[0]) id_time.pop(0) id_time.append(time.time()) request.session[IP] = id_time def process_response(self, request, response): return response
2、CSRF跨站请求伪造
1.CSRF
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,一般缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS很是不一样,XSS利用站点内的信任用户,而CSRF则经过假装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击每每不大流行(所以对其进行防范的资源也至关稀少)和难以防范,因此被认为比XSS更具危险性
能够这样来理解: *攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来讲这个请求是彻底合法的*,可是却完成了攻击者所指望的一个操做,好比以你的名义发送邮件、发消息,盗取你的帐号,添加系统管理员,甚至于购买商品、虚拟货币转帐等。
2.攻击原理
要完成一次CSRF攻击,受害者必须依次完成两个步骤:
1.登陆受信任网站A,并在本地生成Cookie。
2.在不登出A的状况下,访问危险网站B。
看到这里,你也许会说:“若是我不知足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证如下状况不会发生:
1.你不能保证你登陆了一个网站后,再也不打开一个tab页面并访问另外的网站。
2.你不能保证你关闭浏览器了后,你本地的Cookie马上过时,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登陆/结束会话了......)
3.上图中所谓的攻击网站,多是一个存在其余漏洞的可信任的常常被人访问的网站。
3.防护攻击
目前防护 CSRF 攻击主要有三种策略:验证 HTTP Referer 字段;在请求中添加 token 并验证;在 HTTP 头中自定义属性并验证
对于CSRF的防护,Django内部作了一个中间件来处理CSRF的攻击
MIDDLEWARE = [ 'django.middleware.csrf.CsrfViewMiddleware', ]
它设置了一个随机字符串,经过响应发给受信任的用户,当受信任用户发送请求时必须携带如出一辙的随机字符串,才能够进入服务器。并且每次响应服务器给浏览器都不同的,有效的作到了对CSRF的防护
方式一:请求中添加 token 并验证
放在表单中请求
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="" method="post"> {% csrf_token %} <!--渲染以后,得到随机字符串 <input type="hidden" name="csrfmiddlewaretoken" value="RaxCabhuZdpvWxQ67whrdmImNPYuFVQ5Ies9X52TVrIbt1AdVfxPNbMUFVKgLWp4"> --> <input type="text" name="name" placeholder="用户名"> <input type="text" name="pwd" placeholder="密码"> <input type="submit"> </form> </body> </html>
from django.shortcuts import render, HttpResponse def csrf_test(request): if request.method == 'GET': return render(request, 'csrf_test.html') else: name = request.POST.get('name') pwd = request.POST.get('pwd') token = request.POST.get('csrfmiddlewaretoken') print(token) if name == 'xcq' and pwd == '123': msg = '登录成功' else: msg = '登陆失败' return HttpResponse(msg)
经过AJAX
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> {% load static %} <script src="{% static 'jquery-3.3.1.js' %}"></script> </head> <body> <form action="" method="post"> {% csrf_token %} <input type="text" name="name" placeholder="用户名"> <input type="text" name="pwd" placeholder="密码"> </form> <button id="btn">登陆</button> </body> <script> //post请求 $('#btn').click(function () { $.ajax({ url:'/csrf_test/', type:'post', data:{'csrfmiddlewaretoken':$('[name="csrfmiddlewaretoken"]').val()}, //或者 data:{'csrfmiddlewaretoken':'{{ csrf_token }}'}, success:function (data) { alert(data) } }) }) //get请求 $('#btn').click(function () { $.ajax({ url:'/csrf_test/?id={{ csrf_token }}' , type:'get', success:function (data) { alert(data) } }) }) </script> </html>
方式二:在 HTTP 头中自定义属性并验证
//获取cookie里的csrftoken $("#btn").click(function () { var token=$.cookie('csrftoken') $.ajax({ url: '/csrf_test/', headers:{'X-CSRFToken':token}, type: 'post', data: { 'name': $('[name="name"]').val(), 'password': $("#pwd").val(), }, success: function (data) { alert(data) } }) })
局部禁用和局部使用
FBV模式
from django.views.decorators.csrf import csrf_exempt,csrf_protect #局部禁用,全局得使用 @csrf_exempt def csrf_disable(request): print(request.POST) return HttpResponse('ok') #局部使用,全局得禁用 @csrf_protect def csrf_disable(request): print(request.POST) return HttpResponse('ok')
CBV模式
from django.views import View from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt,csrf_protect @method_decorator(csrf_protect,name='dispatch') #CBV的csrf装饰器,只能加载类上且指定方法为dispatch,或dispatch方法上 class Csrf_disable(View): # @method_decorator(csrf_protect) def dispatch(self, request, *args, **kwargs): ret=super().dispatch(request, *args, **kwargs) return ret def get(self,request): return HttpResponse('ok') def post(self,request): return HttpResponse('post---ok')