#中間件是什麼 是介於request與response處理之間的一道處理過程, #它有什麼用 若是你想修改請求,例如被傳送到view中的HttpRequest對象。 或者你想修改view返回的HttpResponse對象,這些均可以經過中間件來實現。 可能你還想在view執行以前作一些操做,這種狀況就能夠用 middleware來實現。
2、自定義中間件javascript
#1在根目錄建立一個py文件,在該文件中寫中間件(建立類) #2在settings中配置中間件,(py文件名.類名) #建立的類繼承MiddlewareMixin from django.utils.deprecation import MiddlewareMixin
#m1.py from django.shortcuts import HttpResponse from django.utils.deprecation import MiddlewareMixin class Middle1(MiddlewareMixin): #建立類並繼承 def process_request(self,request): print('m1 process_request') # return HttpResponse('別往下走了') # def process_response(self,request,response): print('m1 process_response') return response #必定要把返回值傳出去,傳給下一個中間件 class Middle2(MiddlewareMixin): def process_request(self,request): print('m2 process_request') def process_response(self,request,response): print('m2 process_response') return response ------------------------------------- m1 process_request m2 process_request 執行視圖函數 m2 process_response m1 process_response #添加setting 'm1.Middle1', 'm1.Middle2'
經過from import 點進去後發現裏邊的幾個方法 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)
#一、process_request若是有return值,將直接執行當前中間價的process_response、 #二、process_response必須有return值 #三、中間件的process_request方法是在執行視圖函數以前執行的。
以下圖html
例子java
from django.utils.deprecation import MiddlewareMixin class Middle1(MiddlewareMixin): def process_request(self,request): print('m1 process_request') # return HttpResponse('別往下走了) def process_response(self,request,response): print('m1 process_response') return response class Middle2(MiddlewareMixin): def process_request(self,request): print('m2 process_request') def process_response(self,request,response): print('m2 process_response') return response ----------------------------------------------------------------------- m1 process_request m2 process_request 執行視圖函數 m2 process_response m1 process_response #若是把代碼中的註釋打開,輸出結果以下 m1 process_request m1 process_response
def process_view(self, request, callback, callback_args, callback_kwargs): print('Middle1 process_response') #callback實際上是視圖函數,後邊的兩個是試圖函數的參數 ----------------------------------------------- #執行流程 #一、正常狀況下,django的中間件執行流程 從上往下依次全部中間件的process_request, 而後從上往下依次全部中間件的process_view, 而後執行視圖函數 而後從下往上依次全部中間件的process_response, #二、若是process_view調用了視圖函數 def process_view(self, request, callback, callback_args, callback_kwargs): print('Middle1 process_view') return callback(request,callback_args, callback_kwargs) #將從當前中間件的process_view,跳過其餘中間件的process_view,直接執行視圖函數, process_response正常執行
示例jquery
#視圖層 def index(request,*args,**kwargs): print('執行視圖函數') return HttpResponse('ok') ---------------------------------------------------------------- from django.utils.deprecation import MiddlewareMixin class Middle1(MiddlewareMixin): def process_request(self,request): print('m1 process_request') # return HttpResponse('別往下走了') def process_response(self,request,response): print('m1 process_response') return response def process_view(self, request, callback, callback_args, callback_kwargs): print('Middle1 process_view') return callback(request,callback_args, callback_kwargs) class Middle2(MiddlewareMixin): def process_request(self,request): print('m2 process_request') def process_response(self,request,response): print('m2 process_response') return response def process_view(self, request, callback, callback_args, callback_kwargs): print('Middle2 process_view') ------------------------------------- m1 process_request m2 process_request Middle1 process_view Middle2 process_view 執行視圖函數 m2 process_response m1 process_response ------------------------------------- 當把註釋打開時 m1 process_request m2 process_request Middle1 process_view 執行視圖函數 m2 process_response m1 process_response
如圖程序員
#代碼沒有出現異常的時候,不會執行, #當代碼出現異常時,正常執行看下圖,當其有返回值的時候,用戶界面顯示的是其返回值,而且,當前中間件之上的process_exception將再也不執行,直接執行最後一個response
示例ajax
def index(request,*args,**kwargs): int9('1dsfs') #錯誤代碼 print('執行視圖函數') return HttpResponse('ok') ------------------------------------------------- from django.utils.deprecation import MiddlewareMixin class Middle1(MiddlewareMixin): def process_request(self,request): print('m1 process_request') def process_response(self,request,response): print('m1 process_response') return response def process_view(self, request, callback, callback_args, callback_kwargs): print('Middle1 process_view') # return callback(request,callback_args, callback_kwargs) def process_exception(self, request, exception): print('Middle1 process_exception') class Middle2(MiddlewareMixin): def process_request(self,request): print('m2 process_request') def process_response(self,request,response): print('m2 process_response') return response def process_view(self, request, callback, callback_args, callback_kwargs): print('Middle2 process_view') def process_exception(self, request, exception): print('Middle2 process_exception') -------------------------------------------------------------- #此時用戶界面報錯 m1 process_request m2 process_request Middle1 process_view Middle2 process_view Middle2 process_exception Middle1 process_exception m2 process_response m1 process_response #當把註釋打開時,用戶界面顯示的是process_exception返回值 #此時,Middle1 process_exception將再也不執行 m1 process_request m2 process_request Middle1 process_view Middle2 process_view Middle2 process_exception m2 process_response m1 process_response
當views出現錯誤時:django
該方法對視圖函數返回值有要求,必須是一個含有render方法類的對象,纔會執行此方法json
class Test: def __init__(self,status,msg): self.status=status self.msg=msg def render(self): import json dic={'status':self.status,'msg':self.msg} return HttpResponse(json.dumps(dic)) def index(response): return Test(True,'測試')
某些IP訪問服務器的頻率太高,進行攔截,好比限制每分鐘不能超過20次。瀏覽器
若是用戶訪問的是login視圖(放過)安全
若是訪問其餘視圖,須要檢測是否是有session認證,已經有了放行,沒有返回login,這樣就免得在多個視圖函數上寫裝飾器了!
做爲延伸擴展內容,嘗試着讀一下如下兩個自帶的中間件:
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
前提
CSRF(Cross-site request forgery)跨站請求僞造,也被稱爲「One Click Attack」或者Session Riding,一般縮寫爲CSRF或者XSRF,是一種對網站的惡意利用。儘管聽起來像跨站腳本(XSS),但它與XSS很是不一樣,XSS利用站點內的信任用戶,而CSRF則經過假裝來自受信任用戶的請求來利用受信任的網站。與XSS攻擊相比,CSRF攻擊每每不大流行(所以對其進行防範的資源也至關稀少)和難以防範,因此被認爲比XSS更具危險性
能夠這樣來理解:
攻擊者盜用了你的身份,以你的名義發送惡意請求,對服務器來講這個請求是徹底合法的,可是卻完成了攻擊者所指望的一個操做,好比以你的名義發送郵件、發消息,盜取你的帳號,添加系統管理員,甚至於購買商品、虛擬貨幣轉帳等。 以下:其中Web A爲存在CSRF漏洞的網站,Web B爲攻擊者構建的惡意網站,User C爲Web A網站的合法用戶
要完成一次CSRF攻擊,受害者必須依次完成兩個步驟: 1.登陸受信任網站A,並在本地生成Cookie。 2.在不登出A的狀況下,訪問危險網站B。 看到這裏,你也許會說:「若是我不知足以上兩個條件中的一個,我就不會受到CSRF的攻擊」。是的,確實如此,但你不能保證如下狀況不會發生: 1.你不能保證你登陸了一個網站後,再也不打開一個tab頁面並訪問另外的網站。 2.你不能保證你關閉瀏覽器了後,你本地的Cookie馬上過時,你上次的會話已經結束。(事實上,關閉瀏覽器不能結束一個會話,但大多數人都會錯誤的認爲關閉瀏覽器就等於退出登陸/結束會話了......) 3.所謂的攻擊網站,多是一個存在其餘漏洞的可信任的常常被人訪問的網站。
目前防護 CSRF 攻擊主要有三種策略:驗證 HTTP Referer 字段;在請求地址中添加 token 並驗證;在 HTTP 頭中自定義屬性並驗證 (1)驗證 HTTP Referer 字段 根據 HTTP 協議,在 HTTP 頭中有一個字段叫 Referer,它記錄了該 HTTP 請求的來源地址。在一般狀況下,訪問一個安全受限頁面的請求來自於同一個網站,好比須要訪問 http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory,用戶必須先登錄 bank.example,而後經過點擊頁面上的按鈕來觸發轉帳事件。這時,該轉賬請求的 Referer 值就會是轉帳按鈕所在的頁面的 URL,一般是以 bank.example 域名開頭的地址。而若是黑客要對銀行網站實施 CSRF 攻擊,他只能在他本身的網站構造請求,當用戶經過黑客的網站發送請求到銀行時,該請求的 Referer 是指向黑客本身的網站。所以,要防護 CSRF 攻擊,銀行網站只須要對於每個轉帳請求驗證其 Referer 值,若是是以 bank.example 開頭的域名,則說明該請求是來自銀行網站本身的請求,是合法的。若是 Referer 是其餘網站的話,則有多是黑客的 CSRF 攻擊,拒絕該請求。 這種方法的顯而易見的好處就是簡單易行,網站的普通開發人員不須要操心 CSRF 的漏洞,只須要在最後給全部安全敏感的請求統一增長一個攔截器來檢查 Referer 的值就能夠。特別是對於當前現有的系統,不須要改變當前系統的任何已有代碼和邏輯,沒有風險,很是便捷。 然而,這種方法並不是萬無一失。Referer 的值是由瀏覽器提供的,雖然 HTTP 協議上有明確的要求,可是每一個瀏覽器對於 Referer 的具體實現可能有差異,並不能保證瀏覽器自身沒有安全漏洞。使用驗證 Referer 值的方法,就是把安全性都依賴於第三方(即瀏覽器)來保障,從理論上來說,這樣並不安全。事實上,對於某些瀏覽器,好比 IE6 或 FF2,目前已經有一些方法能夠篡改 Referer 值。若是 bank.example 網站支持 IE6 瀏覽器,黑客徹底能夠把用戶瀏覽器的 Referer 值設爲以 bank.example 域名開頭的地址,這樣就能夠經過驗證,從而進行 CSRF 攻擊。 即使是使用最新的瀏覽器,黑客沒法篡改 Referer 值,這種方法仍然有問題。由於 Referer 值會記錄下用戶的訪問來源,有些用戶認爲這樣會侵犯到他們本身的隱私權,特別是有些組織擔憂 Referer 值會把組織內網中的某些信息泄露到外網中。所以,用戶本身能夠設置瀏覽器使其在發送請求時再也不提供 Referer。當他們正常訪問銀行網站時,網站會由於請求沒有 Referer 值而認爲是 CSRF 攻擊,拒絕合法用戶的訪問。 (2)在請求地址中添加 token 並驗證 CSRF 攻擊之因此可以成功,是由於黑客能夠徹底僞造用戶的請求,該請求中全部的用戶驗證信息都是存在於 cookie 中,所以黑客能夠在不知道這些驗證信息的狀況下直接利用用戶本身的 cookie 來經過安全驗證。要抵禦 CSRF,關鍵在於在請求中放入黑客所不能僞造的信息,而且該信息不存在於 cookie 之中。能夠在 HTTP 請求中以參數的形式加入一個隨機產生的 token,並在服務器端創建一個攔截器來驗證這個 token,若是請求中沒有 token 或者 token 內容不正確,則認爲多是 CSRF 攻擊而拒絕該請求。 這種方法要比檢查 Referer 要安全一些,token 能夠在用戶登錄後產生並放於 session 之中,而後在每次請求時把 token 從 session 中拿出,與請求中的 token 進行比對,但這種方法的難點在於如何把 token 以參數的形式加入請求。對於 GET 請求,token 將附在請求地址以後,這樣 URL 就變成 http://url?csrftoken=tokenvalue。 而對於 POST 請求來講,要在 form 的最後加上 <input type=」hidden」 name=」csrftoken」 value=」tokenvalue」/>,這樣就把 token 以參數的形式加入請求了。可是,在一個網站中,能夠接受請求的地方很是多,要對於每個請求都加上 token 是很麻煩的,而且很容易漏掉,一般使用的方法就是在每次頁面加載時,使用 javascript 遍歷整個 dom 樹,對於 dom 中全部的 a 和 form 標籤後加入 token。這樣能夠解決大部分的請求,可是對於在頁面加載以後動態生成的 html 代碼,這種方法就沒有做用,還須要程序員在編碼時手動添加 token。 該方法還有一個缺點是難以保證 token 自己的安全。特別是在一些論壇之類支持用戶本身發表內容的網站,黑客能夠在上面發佈本身我的網站的地址。因爲系統也會在這個地址後面加上 token,黑客能夠在本身的網站上獲得這個 token,並立刻就能夠發動 CSRF 攻擊。爲了不這一點,系統能夠在添加 token 的時候增長一個判斷,若是這個連接是鏈到本身本站的,就在後面添加 token,若是是通向外網則不加。不過,即便這個 csrftoken 不以參數的形式附加在請求之中,黑客的網站也一樣能夠經過 Referer 來獲得這個 token 值以發動 CSRF 攻擊。這也是一些用戶喜歡手動關閉瀏覽器 Referer 功能的緣由。 (3)在 HTTP 頭中自定義屬性並驗證 這種方法也是使用 token 並進行驗證,和上一種方法不一樣的是,這裏並非把 token 以參數的形式置於 HTTP 請求之中,而是把它放到 HTTP 頭中自定義的屬性裏。經過 XMLHttpRequest 這個類,能夠一次性給全部該類請求加上 csrftoken 這個 HTTP 頭屬性,並把 token 值放入其中。這樣解決了上種方法在請求中加入 token 的不便,同時,經過 XMLHttpRequest 請求的地址不會被記錄到瀏覽器的地址欄,也不用擔憂 token 會透過 Referer 泄露到其餘網站中去。
<form action="" method="post"> {% csrf_token %} <p>用戶名:<input type="text" name="name"></p> <p>密碼:<input type="text" name="password"></p> <p><input type="submit"></p> </form>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <script src="/static/jquery-3.3.1.js"></script> <title>Title</title> </head> <body> <form action="" method="post"> {% csrf_token %} <p>用戶名:<input type="text" name="name"></p> <p>密碼:<input type="text" name="password" id="pwd"></p> <p><input type="submit"></p> </form> <button class="btn">點我</button> </body> <script> $(".btn").click(function () { $.ajax({ url: '', type: 'post', data: { 'name': $('[name="name"]').val(), 'password': $("#pwd").val(), 'csrfmiddlewaretoken': $('[name="csrfmiddlewaretoken"]').val() }, success: function (data) { console.log(data) } }) }) </script> </html>
獲取cookie:document.cookie
是一個字符串,能夠本身用js切割,也能夠用jquery的插件
獲取cookie:$.cookie('csrftoken')
設置cookie:$.cookie('key','value')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <script src="/static/jquery-3.3.1.js"></script> <script src="/static/jquery.cookie.js"></script> <title>Title</title> </head> <body> <form action="" method="post"> {% csrf_token %} <p>用戶名:<input type="text" name="name"></p> <p>密碼:<input type="text" name="password" id="pwd"></p> <p><input type="submit"></p> </form> <button class="btn">點我</button> </body> <script> $(".btn").click(function () { var token=$.cookie('csrftoken') //var token='{{ csrf_token }}' $.ajax({ url: '', headers:{'X-CSRFToken':token}, type: 'post', data: { 'name': $('[name="name"]').val(), 'password': $("#pwd").val(), }, success: function (data) { console.log(data) } }) }) </script> </html>
全站禁用:註釋掉中間件 'django.middleware.csrf.CsrfViewMiddleware',
局部禁用:用裝飾器(在FBV中使用)
from django.views.decorators.csrf import csrf_exempt,csrf_protect # 再也不檢測,局部禁用(前提是全站使用) # @csrf_exempt # 檢測,局部使用(前提是全站禁用) # @csrf_protect def csrf_token(request): if request.method=='POST': print(request.POST) return HttpResponse('ok') return render(request,'csrf_token.html')
在CBV中使用:
#cbv-->只能加在dispatch方法或者類上面 #局部禁用,全局得使用 #局部使用,全局得禁用 from django.views import View from django.utils.decorators import method_decorator @method_decorator(csrf_protect,name='dispatch') class Csrf_disable(View): @method_decorator(csrf_protect) def dispatch(self, request, *args, **kwargs): ret=super().dispatch(request, *args, **kwargs) return ret