[Django高階之中介軟體、csrf跨站請求偽造]
Django中介軟體
什麼是中介軟體?
Middleware is a framework of hooks into Django’s request/response processing.
It’s a light, low-level “plugin” system for globally altering Django’s input or output.
-------------------------------------------------------------------------------
中介軟體是一個鉤子到Django請求/響應處理的框架。
它是一個輕量級、低階的“外掛”系統,用於全域性改變Django的輸入或輸出。
官方的說法:中介軟體是一個用來處理Django的請求和響應的框架級別的鉤子。它是一個輕量、低級別的外掛系統,用於在全域性範圍內改變Django的輸入和輸出。每個中介軟體元件都負責做一些特定的功能。
但是由於其影響的是全域性,所以需要謹慎使用,使用不當會影響效能。
說的直白一點中介軟體是幫助我們在檢視函式執行之前和執行之後都可以做一些額外的操作,它本質上就是一個自定義類,類中定義了幾個方法,Django框架會在請求的特定的時間去執行這些方法。
中介軟體介紹和常用內建中介軟體
中介軟體:資料庫中介軟體(mycat,分庫分表),
伺服器中介軟體(tomcat,nginx),
訊息佇列中介軟體(rabbitmq)
django中介軟體(Middleware):介於request與response處理之間的一道處理過程,在全域性上改變django的輸入與輸出
django 內建中介軟體
開啟Django專案的Settings.py檔案,看到下圖的MIDDLEWARE配置項
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', # 安全中介軟體
'django.contrib.sessions.middleware.SessionMiddleware', # 會話中介軟體:處理session
'django.middleware.common.CommonMiddleware', # 站點中介軟體:處理是否帶斜槓的
'django.middleware.csrf.CsrfViewMiddleware', # CSRF保護中介軟體:跨站請求偽造的處理
'django.contrib.auth.middleware.AuthenticationMiddleware', # 認證中介軟體:提供使用者認證服務
'django.contrib.messages.middleware.MessageMiddleware',# 訊息中介軟體:基於cookie或者會話的訊息功能
'django.middleware.clickjacking.XFrameOptionsMiddleware', # X-Frame-Options中介軟體:點選劫持保護
]
# SessionMiddleware原始碼
django.contrib.sessions.middleware.SessionMiddleware
process_request(self, request) # 請求來了會觸發
process_response(self, request, response) # 請求走了會觸發
MIDDLEWARE配置項是一個列表(列表是有序的),列表中是一個個字串,這些字串其實是一個個類,也就是一個個中介軟體。
我們之前已經接觸過一個csrf相關的中介軟體了?我們一開始把他註釋掉,再提交post請求的時候,就不會被forbidden了,後來學會使用csrf_token之後就不再註釋這個中介軟體了。
Django預設有七個中介軟體,但是django暴露給使用者可以自定義中介軟體並且裡面可以寫五種方法
中介軟體可以定義五個方法,分別是:(主要的是process_request和process_response)
1.必須掌握
process_request(self,request)
process_response(self, request, response)
2.瞭解即可
process_view(self, request, view_func, view_args, view_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
以上方法的返回值可以是None或一個HttpResponse物件,如果是None,則繼續按照django定義的規則向後繼續執行,如果是HttpResponse物件,則直接將該物件返回給使用者。
自定義中介軟體:
中介軟體詳情參考 ← 點選檢視
自定義中介軟體需要在Django配置檔案中告訴Django去哪找這個自定義的中介軟體,預設可以放在專案的對應app中,新建一個任意名稱的資料夾eg:middleware,在此資料夾中新建任意名稱的檔案eg:my_middleware.py,然後在此檔案中根據需求自定義五個方法中的任意一個或多個
ps:中介軟體的本質就是含有五個可以修改的內建方法的類,所以自定義的時候需要做的就是先繼承一個Django提供的中介軟體混合的父類(MiddlewareMixin)。
在settings.py的MIDDLEWARE配置項中註冊兩個自定義中介軟體:
注意:自定義中介軟體必須在settings.py的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',
'app01.middleware.MyMiddleWare', # 自定義中介軟體
'app01.middleware.MyMiddleWare1', # 自定義中介軟體
]
process_request方法:(重點)
1.請求來的時候需要經過每一箇中間件裡面的 process_request方法,經過的順序是按照配置檔案中註冊的中介軟體從上往下的順序依次執行
2.如果中介軟體裡面沒有定義該方法,那麼直接跳過執行下一個中介軟體
3.它的返回值可以是None,按正常流程繼續走,交給下一個中介軟體處理,如果該方法返回了Httpresponse物件,那麼請求將不再繼續往後執行,而是直接原路返回(校驗失敗不允許訪問.)
process_request方法就是用來做全域性相關的所有限制功能
1 請求來了觸發它,從上往下依次執行(在setting中中介軟體註冊的位置)
def process_request(self, request):
如果返回None,就繼續往下走
如果返回四件套之一,直接就回去了
2 在這裡面寫請求來了的一些判斷
3 request.META.REMOTE_ADDR # 客戶端地址
4 request.META.HTTP_USER_AGENT # 客戶端型別
#自定義中介軟體示例
from django.utils.deprecation import MiddlewareMixin
class MyMiddleWare(MiddlewareMixin):
def process_request(self, request):
print('請求來了0000')
class MyMiddleWare1(MiddlewareMixin):
def process_request(self, request):
print('請求來了1111')
process_response方法:(重點)
1.響應走的時候需要經過每一箇中間件裡面的 process_response方法,該方法有兩個額外的引數 request, response
2,該方法必須返回一個 HttpResponse物件
- 預設返回的就是形參 response
- 你也可以自己返回自己的
3.順序是按照配置檔案中註冊了的中介軟體從下往上依次經過,如果你沒有定義的話直接跳過執行下一個
# 匯入MiddlewareMixin模組
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, HttpResponse,redirect
# 定義中介軟體的類,它繼承MiddlewareMixin
class MyMiddleWare(MiddlewareMixin):
def process_request(self, request):
print('請求來了0000')
# return HttpResponse('不讓你來了')
def process_response(self, request, response):
print('請求走了0000')
return response # 一定要return response
# 定義中介軟體的類,它繼承MiddlewareMixin
class MyMiddleWare1(MiddlewareMixin):
def process_request(self, request):
print('請求來了1111')
def process_response(self, request, response):
print('請求走了1111')
return response
總結:
當前端發來請求,Django會執行每個中介軟體中的process_request,執行順序按照配置檔案由上至下執行。
響應走的時候,Django會執行每個中介軟體中的 process_response方法,process_response方法是在檢視函式之後執行的,執行順序是按照配置檔案中註冊了的中介軟體從下往上依次執行
process_view、process_exception、process_template_response(瞭解):
process_view
路由匹配成功之後執行檢視函式之前自動觸發
self,request,view_name,*args,**kwargs
process_exception
當檢視函式報錯之後自動執行
self,request,exception
process_template_response
self,request,response
圖函式返回的物件有一個render()方法(或者表明該物件是一個TemplateResponse物件或等價方法)
csrf跨站請求偽造
詳情參考 ← 點選檢視
django中處理csrf
1 中介軟體不要註釋了
2 如果是form表單
<form action="" method="post">
# 表單中直接書寫下列的語句即可
{% csrf_token %}
<p>給誰轉:<input type="text" name="to_user" id="id_name"></p>
<p>轉多少:<input type="text" name="money" id="id_money"></p>
<input type="submit" value="轉賬">
</form>
3 如果是ajax提交:兩種方案
$.ajax({
url: '/transfer/',
method: 'post',
//data: {to_user: $('#id_name').val(), money: $('#id_money').val(),csrfmiddlewaretoken:$('[name="csrfmiddlewaretoken"]').val()},
# 方式1:藉助於{% csrf_token %}生成的標籤(不推薦)
data: {to_user: $('#id_name').val(), money: $('#id_money').val(),csrfmiddlewaretoken:'{{csrf_token}}'},
# 方式2:藉助於模板語法直接獲取
success: function (data) {
console.log(data)
}
})
------------------------------------------------------------
# 方式3:藉助於官方提供的js檔案自動獲取
# js檔案匯入即可 程式碼如下: 直接複製即可!
# 放於static資料夾下的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);
}
}
});
csrf相關裝飾器
csrf_exempt 區域性不校驗csrf
csrf_protect 區域性校驗csrf
from django.views.decorators.csrf import csrf_exempt, csrf_protect
# csrf_exempt 區域性不校驗csrf
# csrf_protect 區域性校驗csrf
# @csrf_protect
# @csrf_exempt
def index(request):
if request.method == 'POST':
username = request.POST.get('username')
money = request.POST.get('money')
target_user = request.POST.get('target_user')
print('%s 給 %s 轉了 %s 元錢'%(username,target_user,money))
return render(request,'index.html')
from django.utils.decorators import method_decorator
from django import views
# @method_decorator(csrf_protect,name='post') # 第二種 行
# @method_decorator(csrf_exempt,name='post') # 第二種 不行
class MyLogin(views.View):
# @method_decorator(csrf_protect) # 第三種 所有的方法都會加上該功能
@method_decorator(csrf_exempt) # 第三種 所有的方法都會加上該功能
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request,*args,**kwargs)
def get(self,request):
return HttpResponse('from get')
# @method_decorator(csrf_protect) # 第一種 行
# @method_decorator(csrf_exempt) # 第一種 不行
def post(self,request):
return HttpResponse('from post')
"""
csrf_exempt該裝飾器在CBV中只能給dispatch裝才能生效
"""