[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裝才能生效
"""