1. 程式人生 > >7.Django CSRF 中介軟體

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