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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM