7.Django CSRF 中介軟體
CSRF
1.概述
CSRF(Cross Site Request Forgery)跨站點偽造請求,舉例來講,某個惡意的網站上有一個指向你的網站的連結,如果某個使用者已經登入到你的網站上了,那麼當這個使用者點選這個惡意網站上的那個連結時,就會向你的網站發來一個請求,你的網站會以為這個請求是使用者自己發來的,其實呢,這個請求是那個惡意網站偽造的。
為了避免上面情況的出現,Django引用了CSRF防護機制;Django第一次響應來自某個客戶端的請求時,會在伺服器端隨機生成一個 token,並把這個 token 放在 cookie 裡。然後每次 POST 請求都會帶上這個 token,這樣就能避免被 CSRF 攻擊。如果POST請求中沒有token隨機字串,則返回403拒絕服務
- 在返回的 HTTP 響應的 cookie 裡,django 會為你新增一個 csrftoken 欄位,其值為一個自動生成的 token
- 在所有的 POST 表單時,必須包含一個 csrfmiddlewaretoken 欄位 (只需要在模板里加一個 tag, django 就會自動幫你生成,見下面)
- 在處理 POST 請求之前,django 會驗證這個請求的 cookie 裡的 csrftoken 欄位的值和提交的表單裡的 csrfmiddlewaretoken 欄位的值是否一樣。如果一樣,則表明這是一個合法的請求,否則,這個請求可能是來自於別人的 csrf 攻擊,返回 403 Forbidden。
- 在所有 ajax POST 請求裡,新增一個 X-CSRFTOKEN header,其值為 cookie 裡的 csrftoken 的值
2.啟用方式
settings裡面全域性啟用
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', ]
區域性使用方法
先匯入
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_protect,為當前函式強制設定防跨站請求偽造功能,即便settings中沒有設定全域性中介軟體。
@csrf_exempt,取消當前函式防跨站請求偽造功能,即便settings中設定了全域性中介軟體。
3.form表單提交POST請求
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="post"> {% csrf_token %} <input type="text" name="user" /> <input type="text" name="pwd" /> <input type="checkbox" name="session" value="1"/> <input type="submit" value="提交" /> </form> </body> </html>
from django.shortcuts import render,HttpResponse,redirect def login(request): if request.method == 'GET': return render(request ,'login.html') elif request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') if user == 'root' and pwd == "123": # 生成隨機字串 # 寫到使用者瀏覽器Cookie # 儲存到Session中 # 在隨機字串對應的字典中設定相關內容... request.session['username'] = user request.session['if_login'] = True # 可不加 直接判斷username也可以 if request.POST.get('session') == '1': # 單獨設定超時時間,當前session生效,不影響全域性 request.session.set_expiry(10) # 10秒 return redirect('/index/') else: return redirect('/login/') def index(request): # 獲取當前使用者的隨機字串 # 根據隨機字串獲取對應資訊 if request.session.get('if_login'): return render(request, 'index.html') else: return redirect('/login/')views.py
4.Ajax提交POST請求
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="post"> {% csrf_token %} <input type="text" name="user" /> <input type="text" name="pwd" /> <input type="checkbox" name="session" value="1"/> <input type="submit" value="提交" /> <input id='btn' type="button" value="Ajax提交" /> </form> <script src="/static/jquery-1.12.4.js"></script> <script src="/static/jquery.cookie.js"></script> <script> $(function () { $('#btn').click(function () { $.ajax({ url:'/login/', type:'POST', data:{'user':'root','pwd':'123'}, headers:{'X-CSRFtoken':$.cookie('csrftoken')}, success:function (arg) { } }) }) }) </script> </body> </html>
上面html檔案點選提交後,執行成功;但是往往一個程式不止一個Ajax請求,所以我們需要對每個Ajax請求都新增headers請求頭,這樣未免增加很多工作量;這時就需要做全域性設定,不必每個都新增請求頭
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/login/" method="post"> {% csrf_token %} <input type="text" name="user" /> <input type="text" name="pwd" /> <input type="checkbox" name="session" value="1"/> <input type="submit" value="提交" /> <input id='btn' type="button" value="Ajax提交" /> </form> <script src="/static/jquery-1.12.4.js"></script> <script src="/static/jquery.cookie.js"></script> <script> $(function () { {# 全域性配置,所有Ajax請求都先執行下面操作#} $.ajaxSetup({ beforeSend:function (xhr,settings) { xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken')); } }); $('#btn').click(function () { $.ajax({ url:'/login/', type:'POST', data:{'user':'root','pwd':'123'}, success:function (arg) { } }) }) }) </script> </body> </html>
中介軟體
1.概述
django 中的中介軟體(middleware),在django中,中介軟體其實就是一個類,在請求到來和結束後,django會根據自己的規則在合適的時機執行中介軟體中相應的方法;在django專案的settings模組中,有一個 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', ]
2.中介軟體的五種方法
(1)process_request(self,request)
請求來時執行,不寫時直接跳過,執行下一個中介軟體;當有return HttpResonse時,下面中介軟體不再執行
(2)process_view(self, request, callback, callback_args, callback_kwargs)
先執行process_request,執行完後,再從起始執行proces_view
(3)process_template_response(self,request,response)
如果Views中的函式返回的物件中,具有render方法,此方法執行
(4)process_exception(self, request, exception)
異常觸發執行,當views.py函式執行出錯後,此方法執行;出錯時,最低層的exception優先順序最高,執行最近的一個,
然後執行respnse方法
(5)process_response(self, request, response)
請求返回時執行,不寫時直接跳過,執行下一個中介軟體;當有return HttpResonse時,會替換原資料
以上方法的返回值可以是None和HttpResonse物件,如果是None,則繼續按照django定義的規則向下執行,如果是HttpResonse物件,則直接將該物件返回給使用者
3.自定義中介軟體
Django主目錄下建立middleware目錄(名字任意),在目錄下建立m.py檔案
1.process_request、process_response的使用
(1)views.py
def test(request): print('測試中介軟體') return HttpResponse('ok')
(2)m.py
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class Row1(MiddlewareMixin): #繼承MiddlewareMixin類 def process_request(self,request): print('第一個中介軟體') # return HttpResponse('滾') #加return,到這裡就不會繼續往下走了 def process_response(self,request,response): print('返回資訊3') return response class Row2(MiddlewareMixin): # 繼承MiddlewareMixin類 def process_request(self, request): print('第二個中介軟體') def process_response(self, request, response): print('返回資訊2') return response class Row3(MiddlewareMixin): # 繼承MiddlewareMixin類 def process_request(self, request): print('第三個中介軟體') def process_response(self, request, response): print('返回資訊1') return response
(3)settings裡面加上自定義的中介軟體
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.m.Row1', 'middleware.m.Row2', 'middleware.m.Row3', ]
(4)執行結果
第一個中介軟體
第二個中介軟體
第三個中介軟體
測試中介軟體
返回資訊1
返回資訊2
返回資訊3
2.process_view
m.py
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class Row1(MiddlewareMixin): #繼承MiddlewareMixin類 def process_request(self,request): print('第一個中介軟體') def process_view(self, request, view_func, view_func_args, view_func_kwargs): print('view11') def process_response(self,request,response): print('返回資訊3') return response class Row2(MiddlewareMixin): # 繼承MiddlewareMixin類 def process_request(self, request): print('第二個中介軟體') def process_view(self, request, view_func, view_func_args, view_func_kwargs): print('view22') def process_response(self, request, response): print('返回資訊2') return response class Row3(MiddlewareMixin): # 繼承MiddlewareMixin類 def process_request(self, request): print('第三個中介軟體') def process_view(self, request, view_func, view_func_args, view_func_kwargs): print('view33') def process_response(self, request, response): print('返回資訊1') return response
執行結果:
第一個中介軟體
第二個中介軟體
第三個中介軟體
view11
view22
view33
測試中介軟體
返回資訊1
返回資訊2
返回資訊3