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')