1. 程式人生 > 資訊 >投資 370 億,騰訊全球總部開建:內網徵名第一叫鵝城

投資 370 億,騰訊全球總部開建:內網徵名第一叫鵝城

目錄

django之中介軟體

一 中介軟體簡介

# 中介軟體顧名思義,是介於request與response處理之間的一道處理過程,相對比較輕量級,並且在全域性上改變django的輸入與輸出。因為改變的是全域性,所以需要謹慎實用,用不好會影響到效能

# django內建中介軟體
	'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預設的中介軟體:
-在django專案的settings模組中,有一個 MIDDLEWARE_CLASSES 變數,其中每一個元素就是一箇中間件
# 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.

二 自定義中介軟體

1 自定義步驟:

1)-寫一個類,繼承MiddlewareMixin
2)-裡面寫方法process_request(請求來了,一定會觸發它的執行)
3)-在setting中配置(注意,放在前和放在後)
MIDDLEWARE = [
    ...
    'app01.mymiddle.MyMiddleware1',
    ...
]
# MiddlewareMixin類
# from django.utils.deprecation import MiddlewareMixin
class MiddlewareMixin:
    def __init__(self, get_response=None):
        self.get_response = get_response
        super().__init__()

    def __call__(self, request):
        response = None
        if hasattr(self, 'process_request'):
            response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        if hasattr(self, 'process_response'):
            response = self.process_response(request, response)
        return response

2 中介軟體主要方法:

# 1) 中介軟體中主要有幾個方法:
	process_request(self,request)
	process_response(self, request, response)
----------------------------------------------
	process_view(self, request, callback, callback_args, callback_kwargs)
	process_exception(self, request, exception)
----------------------------------------------
	process_template_response(self,request,response)

3 process_request和process_response

0) 是使用最多的兩種方法!!
1) process_request(request物件)
2) process_response(request物件,response物件)

3) 多箇中間件,執行順序是什麼?
	-請求來的時候,從上往下執行:process_request
    -請求走的時候,從下往上執行:process_response

4) process_request可以幹什麼?【應用場景】
	-寫一箇中間件,不管前端用什麼編碼,在requset.data中都有post的資料
    -頻率限制(限制某個ip地址,一分鐘只能訪問5次)
    -登入認證(只要沒登入,重定向到login路徑)、
    -記錄使用者訪問日誌(ip,時間,訪問路徑)
    
5) process_response可以幹什麼?內部有response物件【應用場景】
	-統一給所有(某幾個路徑)加cookie
    -統一給所有(某幾個路徑)加響應頭

6) process_request和process_response執行流程:
	-當用戶發起請求的時候會依次經過所有的的中介軟體,這個時候的請求是process_request, 最後到達views的函式中,views函式處理後,再依次穿過中介軟體,這個時候是process_response,最後返回給請求者。
上述截圖中的中介軟體都是django中的,我們也可以自己定義一箇中間件,我們可以自己寫一個類,但是必須繼承MiddlewareMixin。

7) 自定義中介軟體的詳細執行流程
-7.1) 第一步:匯入
	-from django.utils.deprecation import MiddlewareMixin

-7.2) 第二步:自定義中介軟體程式碼
from django.utils.deprecation import MiddlewareMixin
# process_request
# process_response
from django.shortcuts import render, HttpResponse

class MyMiddleware1(MiddlewareMixin):
    # 所有的請求來了,都會走到它
    def process_request(self, request):
        print('請求來了1')
        # print(request.session)
        # 可不可以返回?必須返回HttpResponse的物件
        # return HttpResponse('我不讓你訪問')  # 執行到這裡就直接返回,後續的程式碼不再執行
    # 所有的請求走了,都會執行它
    def process_response(self, request, response):
        print('請求走了1')
        # response['name'] = 'lxx'
        # print(response)  # <HttpResponse status_code=200, "text/html; charset=utf-8">
        return response  # 此處必須return這個response,不寫的話預設返回None,後續中介軟體會用到response,找不到就會報錯

class MyMiddleware2(MiddlewareMixin):
    # 所有的請求來了,都會走到它
    def process_request(self, request):
        print('請求來了2')
        # return HttpResponse('dsb, 檢視函式不執行了吧')
    # 所有的請求走了,都會執行它
    def process_response(self, request, response):
        print('請求走了2')
        return response

-7.3) 在view中定義一個檢視函式(index)
def index(request):
    print('我是檢視函式')
    return HttpResponse('ok')

-7.4) 第四步:在settings.py的MIDDLEWARE裡註冊自己定義的中介軟體
# 執行結果
"""請求來了1
請求來了2
我是檢視函式
請求走了2
請求走了1"""

8) 注意:
-如果當請求到達請求2的時候直接不符合條件返回,即return HttpResponse("Md2中斷"),程式將把請求直接發給中介軟體2返回,然後依次返回到請求者,結果如下:
-返回Md2中斷的頁面,後臺列印如下:
"""
Md1請求
Md2請求
Md2返回
Md1返回"""
流程圖如下:
9) 總結:
-中介軟體的process_request方法是在執行檢視函式之前執行的。
-當配置多箇中間件時,會按照MIDDLEWARE中的註冊順序,也就是列表的索引值,從前到後依次執行的。
-不同中介軟體之間傳遞的request都是同一個物件
-多箇中間件中的process_response方法是按照MIDDLEWARE中的註冊順序倒序執行的,也就是說第一個中介軟體的process_request方法首先執行,而它的process_response方法最後執行,最後一箇中間件的process_request方法最後一個執行,它的process_response方法是最先執行。

4 process_view

-Django會在路由匹配成功,呼叫檢視函式之前呼叫process_view方法。
def process_view(self, request, callback, callback_args, callback_kwargs):
# 該方法有四個引數
    -request		# 是HttpRequest物件。
    -callback 		# 是Django即將使用的檢視函式。 (它是實際的函式物件,而不是函式的名稱作為字串。)
    -callback_args 	# 是將傳遞給檢視的位置引數的列表.
    -callback_kwargs # 是將傳遞給檢視的關鍵字引數的字典。 view_args和view_kwargs都不包含第一個檢視引數(request)。

-它應該返回None或一個HttpResponse物件。 如果返回None,Django將繼續處理這個請求,執行任何其他中介軟體的process_view方法,然後在執行相應的檢視。 如果它返回一個HttpResponse物件,Django不會呼叫適當的檢視函式。 它將執行中介軟體的process_response方法並將應用到該HttpResponse並返回結果。
# process_view正常的執行:
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, HttpResponse

class MyMiddleware1(MiddlewareMixin):
    # 所有的請求來了,都會走到它
    def process_request(self, request):
        print('請求來了1')
        # print(request.session)
        # 可不可以返回?必須返回HttpResponse的物件
        # return HttpResponse('我不讓你訪問')  # 執行到這裡就直接返回,後續的程式碼不再執行

    # 所有的請求走了,都會執行它
    def process_response(self, request, response):
        print('請求走了1')
        # response['name'] = 'lxx'
        # print(response)  # <HttpResponse status_code=200, "text/html; charset=utf-8">
        return response  # 此處必須return這個response,不寫的話預設返回None,後續中介軟體會用到response,找不到就會報錯

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print('中介軟體1的process_view')

class MyMiddleware2(MiddlewareMixin):
    # 所有的請求來了,都會走到它
    def process_request(self, request):
        print('請求來了2')
        # return HttpResponse('dsb, 檢視函式不執行了吧')

    # 所有的請求走了,都會執行它
    def process_response(self, request, response):
        print('請求走了2')
        return response

# 執行結果:
"""請求來了1
請求來了2
中介軟體1的process_view
我是檢視函式
請求走了2
請求走了1"""
# 執行流程圖:
# process_view可以用來呼叫檢視函式:
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, HttpResponse

class MyMiddleware1(MiddlewareMixin):
    # 所有的請求來了,都會走到它
    def process_request(self, request):
        print('請求來了1')
        # print(request.session)
        # 可不可以返回?必須返回HttpResponse的物件
        # return HttpResponse('我不讓你訪問')  # 執行到這裡就直接返回,後續的程式碼不再執行

    # 所有的請求走了,都會執行它
    def process_response(self, request, response):
        print('請求走了1')
        # response['name'] = 'lxx'
        # print(response)  # <HttpResponse status_code=200, "text/html; charset=utf-8">
        return response  # 此處必須return這個response,不寫的話預設返回None,後續中介軟體會用到response,找不到就會報錯

    def process_view(self, request, callback, callback_args, callback_kwargs):
        res = callback(request)
        print('中介軟體1的process_view')
        return res

# 執行結果:
"""請求來了1
請求來了2
我是檢視函式
中介軟體1的process_view
請求走了2
請求走了1"""

# 注意:
-process_view如果有返回值,會越過其他的process_view以及檢視函式,但是所有的process_response都還會執行。

5 process_exception

-檢視函數出錯,緊後會執行它(全域性異常捕獲)("記錄日誌,哪個ip地址,訪問哪個路徑,出的錯")# 全域性異常捕獲,返回4開頭的
def process_exception(self, request, exception):
# 該方法兩個引數:
    -一個HttpRequest物件
    -一個exception是檢視函式異常產生的Exception物件。

	-這個方法只有在檢視函式中出現異常了才執行,它返回的值可以是一個None也可以是一個HttpResponse物件。如果是HttpResponse物件,Django將呼叫模板和中介軟體中的process_response方法,並返回給瀏覽器,否則將預設處理異常。如果返回一個None,則交給下一個中介軟體的process_exception方法來處理異常。它的執行順序也是按照中介軟體註冊順序的倒序執行。
# process_exception方法來處理異常:
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, HttpResponse

class MyMiddleware1(MiddlewareMixin):
    # 所有的請求來了,都會走到它
    def process_request(self, request):
        print('請求來了1')
        # print(request.session)
        # 可不可以返回?必須返回HttpResponse的物件
        # return HttpResponse('我不讓你訪問')  # 執行到這裡就直接返回,後續的程式碼不再執行

    # 所有的請求走了,都會執行它
    def process_response(self, request, response):
        print('請求走了1')
        # response['name'] = 'lxx'
        # print(response)  # <HttpResponse status_code=200, "text/html; charset=utf-8">
        return response  # 此處必須return這個response,不寫的話預設返回None,後續中介軟體會用到response,找不到就會報錯

    def process_view(self, request, callback, callback_args, callback_kwargs):
        # res = callback(request)
        print('中介軟體1的process_view')
        # return res

class MyMiddleware2(MiddlewareMixin):
    # 所有的請求來了,都會走到它
    def process_request(self, request):
        print('請求來了2')
        # return HttpResponse('dsb, 檢視函式不執行了吧')

    # 所有的請求走了,都會執行它
    def process_response(self, request, response):
        print('請求走了2')
        return response

    # 全域性異常捕獲,返回4開頭的
    def process_exception(self, request, exception):
        print(exception)
        print('exception')
        return HttpResponse('error')
        # return render(request, 'error.html')

[views.py]:
from django.shortcuts import render, HttpResponse

def index(request):
    print('我是檢視函式')
    a
    return HttpResponse('ok')
    # return render(request, 'index.html')

# 執行結果:
"""請求來了1
請求來了2
中介軟體1的process_view
我是檢視函式
name 'a' is not defined
exception
請求走了2
請求走了1"""

# 流程圖如下:
# 總結:
	-以檢視函式為中間點,檢視函式之前的 process_request和 process_view都是"自上至下"執行,
    -檢視函式之後的 process_exception和 process_response都是"自下至上"執行。

6 process_template_response

-使用較少:
-該方法對檢視函式返回值有要求,必須是一個含有render方法類的物件,才會執行此方法

三 CSRF_TOKEN跨站請求偽造

1 什麼是跨站請求偽造

# 攻擊者盜用了你的身份,以你的名義傳送惡意請求,對伺服器來說這個請求是完全合法的。

2 django中介軟體解決csrf攻擊

# 中介軟體的使用:django.middleware.csrf.CsrfViewMiddleware
	django中介軟體會瀏覽器在傳送post請求的時候,給請求加上一個k:v形式的csrf_token字串返回瀏覽器,並且有一個過期時間,下次再請求的時候需要驗證這個字串,沒有字串的請求就會拒絕訪問。
# 1) form表單提交
	-在form表單中 {% csrf_token %}
# 檢視函式
def csrf_test(request):
    if request.method == 'GET':
        return render(request, 'csrf_test.html')
    else:
        name = request.POST.get('name')
        password = request.POST.get('password')
        print(name)
        print(password)
        return HttpResponse('登入成功!')
# HTML
<form action="" method="post">
    {% csrf_token %}
    <p>使用者名稱:<input type="text" name="name"></p>
    <p>密碼:<input type="password" name="password"></p>
    <p><input type="submit" value="提交"></p>
</form>
# 2) ajax提交
# html
{% csrf_token %}
<p>使用者名稱:<input type="text" name="name"></p>
<p>密碼:<input type="password" name="password"></p>
<p><input type="submit" value="提交" id="submit"></p>
// 方式一:放到data中
    $('#submit').click(function () {
        $.ajax({
            url: '/app01/csrf_test/',
            method: 'post',
            data: {'name': $('[name = "name"]').val(),
                'password': $('[name = "password"]').val(),
                'csrfmiddlewaretoken': $('[name = "csrfmiddlewaretoken"]').val(),
            },
            success: function (data) {
                console.log('成功了')
                console.log(data)
            },
            error: function (data) {
                console.log('lxxxxxxxxx')
                console.log(data)
            }
        })
    })

// 方式二:放到data中
	'csrfmiddlewaretoken':'{{ csrf_token }}'

// 方式三:放到請求頭中
    $('#submit').click(function () {
        $.ajax({
            url: '/app01/csrf_test/',
            method: 'post',
            headers:{'X-CSRFToken':'{{csrf_token}}'},
            data: {'name': $('[name = "name"]').val(),
                'password': $('[name = "password"]').val(),
                // 'csrfmiddlewaretoken': $('[name = "csrfmiddlewaretoken"]').val(),  //必須加,不然後403forbidden
                // 'csrfmiddlewaretoken': '{{ csrf_token }}', //必須引號,否則就不是字串了
            },
            success: function (data) {
                console.log('成功了')
                console.log(data)
            },
            error: function (data) {
                console.log('lxxxxxxxxx')
                console.log(data)
            }
        })
    })
# 3) 使用jquery.cookie.js
	-從cookie中取,則網頁中的{% csrf_token %}就不需要了
	-在瀏覽器中對cookie進行增,刪,查,改
    -前後端分離(js操作cookie)

3 csrf的區域性使用、區域性禁用

# 如何區域性使用/禁用csrf
	-在檢視函式上加裝飾器
    from django.views.decorators.csrf import csrf_exempt,csrf_protect
# 全域性啟用,區域性禁用
# (中介軟體不能註釋,這個檢視函式,已經沒有csrf校驗了)
@csrf_exempt
def csrf_test(request):
    if request.method == 'GET':
        return render(request, 'csrf_test.html')
    else:
        name = request.POST.get('name')
        password = request.POST.get('password')
        print(name)
        print(password)
        return HttpResponse('登入成功!')
# 全域性禁用,區域性使用csrf
# (中介軟體註釋掉,不傳csrf_token本可以正常使用,加上這個裝飾器,就必須帶上csrf_token才能使用了)
@csrf_protect
def csrf_test(request):
    if request.method=='GET':
        return render(request,'csrf_test.html')
    else:
        name=request.POST.get('name')
        password=request.POST.get('password')
        print(name)
        print(password)
        return HttpResponse('登入成功')
# 古怪的使用方式,在urls.py中
# 裝飾器的原理就是把被裝飾的函式當引數傳入,這個方式直接在路由裡給函式裝飾
path('csrf_test/', csrf_exempt(views.csrf_test))