1. 程式人生 > >Django(六)Session、CSRF、中介軟體

Django(六)Session、CSRF、中介軟體

大綱

二、session
1、session與cookie對比
2、session基本原理及流程
3、session伺服器操作(獲取值、設定值、清空值)
4、session通用配置(在配置檔案中)
5、session引擎配置(db、cache、file、cookie加密)
三、CSRF
1、csrf原理-form提交及ajax提交
2、csrf全域性與區域性應用配置
四、中介軟體生命週期
1、process_request、process_response
下面自己建立一箇中間件
2、process_view
3、其他
一、內容回顧

二、session

1、session與cookie對比

Cookie:儲存在使用者瀏覽器端的鍵值對

本地可以修改;如果有敏感資訊,可以被看到。

基於cookie做使用者驗證時,敏感資訊不適合放在cookie中
把儲存壓力放到每個客戶端上,對於伺服器端壓力小了。

Session:儲存在伺服器端的鍵值對

服務端:儲存鍵值對{'隨機字串':{……使用者資訊……}},通過cookie儲存隨機字串到客戶端上。

使用session前:先執行python manage.py makemigrations python manage.py migrate,因為預設Django session 儲存在資料庫中的django_session表中。

2、session基本原理及流程

  • session 設定值

    • request.session['is_login'] = True Django會執行一下操作
   # 生成隨機字串
   # 寫到使用者瀏覽器cookie
   # 儲存到session中
   # 在隨機字串對應的字典中設定相關內容……
  • session 獲取值

    • request.session['is_login']: 執行以下操作
  # 獲取當前使用者的隨機字串
  # 根據隨機字串獲取對應資訊
  • 使用session前,別忘了先生成資料庫

urls.py

    url(r'^login/'
, views.login), url(r'^index/', views.index), url(r'^logout/$', views.logout),

views.py

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":
        # session中設定值
            # 生成隨機字串
            # 寫到使用者瀏覽器cookie
            # 儲存到session中
            # 在隨機字串對應的字典中設定相關內容……
            request.session['username'] = user
            request.session['is_login'] = True
            if request.POST.get('rmb',None) == '1':
                # 超時時間
                request.session.set_expiry(10)
            return redirect('/index/')
        else:
            return render(request,'login.html')

def index(request):
    # 獲取當前使用者的隨機字串
    # 根據隨機字串獲取對應資訊
    if request.session.get('is_login',None):
        return HttpResponse(request.session['username'])
    else:
        return HttpResponse("請登入……")

def logout(request):
    # del request.session['username']
    request.session.clear()    # 清除session,登出
    return redirect('/login/')

login.html

<body>
    <form action="/login/" method="POST">
        <input type="text" name="user" />
        <input type="text" name="pwd" />
        <input type="checkbox" name="rmb" value="1" /> 10秒免登入
        <input type="submit" value="提交" />
    </form>
</body>>  

index.html

<body>
    <h1>歡迎登入:{{ username }}, {{ request.session.username }}</h1>
<!----------  這裡不用使用後臺模板傳值,使用session也能獲取到使用者名稱  -------------->
    <a href="/logout/">登出</a>
</body>

3、session伺服器操作(獲取值、設定值、清空值)

    # 獲取、設定、刪除Session中資料
        request.session['k1']               # 獲取
        request.session.get('k1',None)
        request.session['k1'] = 123         # 設定
        request.session.setdefault('k1',123) # 存在則不設定
        del request.session['k1']           # 刪除

    # 所有 鍵、值、鍵值對
        request.session.keys()
        request.session.values()
        request.session.items()
        request.session.iterkeys()
        request.session.itervalues()
        request.session.iteritems()

    # 獲取使用者session的隨機字串
        request.session.session_key
    # 將所有Session失效日期小於當前日期的資料刪除
        request.session.clear_expired()
    # 檢查 使用者session的隨機字串 在資料庫中是否存在
        request.session.exists("session_key")
    # 刪除當前使用者的所有Session資料
        request.session.delete("session_key")
        request.session.clear()     # 比delete用法更簡單,登出的時候可以使用

# 設定超時時間 (預設session超時時間是兩週)
    request.session.set_expiry(value)
        # * 如果value是個整數,session會在些秒數後失效。
        # * 如果value是個datatime或timedelta,session就會在這個時間後失效。
        # * 如果value是0,使用者關閉瀏覽器session就會失效。
        # * 如果value是None,session會依賴全域性session失效策略。

4、session通用配置(在配置檔案中)

settings.py

SESSION_ENGINE = 'django.contrib.sessions.backends.db'  # 引擎(預設)

SESSION_COOKIE_NAME = "sessionid"       # Session的cookie儲存在瀏覽器上時的key,即:sessionid=隨機字串(預設)
SESSION_COOKIE_PATH = "/"               # Session的cookie儲存的路徑(預設)
SESSION_COOKIE_DOMAIN = None             # Session的cookie儲存的域名(預設)
SESSION_COOKIE_SECURE = False            # 是否Https傳輸cookie(預設)
SESSION_COOKIE_HTTPONLY = True           # 是否Session的cookie只支援http傳輸(預設)
SESSION_COOKIE_AGE = 1209600             # Session的cookie失效日期(2周)(預設)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False  # 是否關閉瀏覽器使得Session過期(預設)
SESSION_SAVE_EVERY_REQUEST = False       # 是否每次請求都儲存Session,預設修改之後才儲存(預設)
# 這個好。settings裡設為true,超時時間按照最後一次客戶端請求計算,如上按照最後一次請求之後10秒失效。

5、session引擎配置(db、cache、file、cookie加密)

Django中預設支援Session,其內部提供了5種類型的Session供開發者使用:

  • 資料庫(預設)
  • 快取
  • 檔案
  • 快取+資料庫
  • 加密cookie
# 資料庫Session
    SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(預設)

# 快取Session    
    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
    SESSION_CACHE_ALIAS = 'default'  # 使用的快取別名(預設記憶體快取,也可以是memcache),此處別名依賴快取的設定
    # 連線memcache 的配置,快取部分會提到。不支援redis,連它需要安裝外掛

# 檔案Session
    SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
    SESSION_FILE_PATH = os.path.join(BASE_DIR, 'cache')  # 放到cache目錄下
    SESSION_FILE_PATH = None   # 快取檔案路徑,如果為None,則使用tempfile模組獲取一個臨時地址tempfile.gettempdir()
    # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T

# 快取+資料庫Session
    SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'  # 引擎

# 加密cookie Session (都放到cookie裡面,只是做了加密處理)
    SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'  # 引擎

三、CSRF

之前提到的xss攻擊:網站評論裡等允許別人寫js的時候,別人進行的惡意操作。csrf類似。

CSRF的防護通常有兩種方式,一個是通過Challenge-Response的方式,例如通過Captcha和重新輸入密碼等方式來驗證請求是否偽造,但這會影響使用者體驗,類似銀行付款會採用這樣的方式。另一種是通過隨機Token的方式,多數Web系統都會採用這種方式,Django也是用的這種。

1、csrf原理-form提交及ajax提交

客戶端get請求時,django會生成隨機字串給客戶,當客戶端提交form表單的時候,如果沒有隨機字串,則django不允許,報403錯誤。

form提交資料需要帶隨機字串過去,Ajax提交也需要帶著過去。ajax帶哪的值?

加上{% csrf_token %}在html form裡生成了一個,在也生成了一份。瀏覽器審查元素 –> Network –> Cookies 裡也能找到隨機字串。

所以ajax提交的時候,只需要把cookie裡的隨機字串拿到,放到請求頭裡面發過去就可以了

後臺獲取隨機字串的key,key是什麼值? x-CSRFtoken

login.html

<body>
    <!-- form 提交 -->
    <form action="/login/" method="POST">
        <!-- 生成 csrf 的隨機字串 -->
        <!-- {{ csrf_token }} -->
        {% csrf_token %}  <!--html裡會自動生成隱藏input框-->
        <input type="text" name="user" />
        <input type="text" name="pwd" />
        <input type="checkbox" name="rmb" value="1" /> 10秒免登入
        <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({  // 對整個頁面所有的ajax操作做個配置
                beforeSend: function(xhr,settings){  // 傳送ajax前的操作
                    xhr.setRequestHeader('X-CSRFtoken', $.cookie('csrftoken'));
                }
            });

            $('#btn').click(function () {
                $.ajax({
                    url: '/login/',
                    type:"POST",
                    data: {'user': 'root', 'pwd': '123'},
        // 這種方式加請求頭,每個操作都加麻煩一些,所以使用上面的  ajaxSetup 里加
                    // headers: {'X-CSRFtoken': $.cookie('csrftoken')},
                    success:function(arg){

                    }
                })
            });
        })
    </script>
</body>

這裡ajax提交,在瀏覽器審查元素、network裡看效果。

2、csrf全域性與區域性應用配置

settings裡面,允許csrf驗證,那麼就是對全域性都進行驗證。如果個別的方法不需要使用,怎麼單一配置?

django為使用者實現防止跨站請求偽造的功能,通過中介軟體 django.middleware.csrf.CsrfViewMiddleware 來完成。而對於django中設定防跨站請求偽造功能有分為全域性和區域性。

全域性:

  中介軟體 django.middleware.csrf.CsrfViewMiddleware

區域性:

  • @csrf_protect,為當前函式強制設定防跨站請求偽造功能,即便settings中沒有設定全域性中介軟體。
  • @csrf_exempt,取消當前函式防跨站請求偽造功能,即便settings中設定了全域性中介軟體。

注:from django.views.decorators.csrf import csrf_exempt,csrf_protect

對於ajax中,採用ajaxSetup方式也是全域性都加,比如get方式是不需要的,可以如下配置

        var csrftoken = $.cookie('csrftoken');

        function csrfSafeMethod(method) {
            // these HTTP methods do not require CSRF protection
            return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
        }
        $.ajaxSetup({
            beforeSend: function(xhr, settings) {
                // settings 會獲取到ajax裡面的所有配置
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                }
            }
        });
        function Do(){

            $.ajax({
                url:"/app01/test/",
                data:{id:1},
                type:'POST',
                success:function(data){
                    console.log(data);
                }
            });

        }

四、中介軟體生命週期

settings裡的 MIDDLEWARE 都是一個一個的中介軟體。客戶端請求,先經過一排一排的中介軟體到達views,之後再通過中介軟體返回給客戶端。而通過中介軟體都是呼叫中介軟體的某個方法、

1、process_request、process_response

  • process_request:客戶端請求,經過中介軟體的方法
  • process_response:返回客戶端,經過中介軟體的方法

下面自己建立一箇中間件

隨便建立一個目錄middle,

middle/m.py

from django.utils.deprecation import MiddlewareMixin

class Row1(MiddlewareMixin):
    def process_request(self, request):
        print("中介軟體1")
    def process_response(self, request, response):
        print("中介軟體1返回")
        return response
# 引數裡的 response :就是views裡面返回的值,所以要繼續返回一下,否則客戶端收不到資料
from django.shortcuts import HttpResponse
class Row2(MiddlewareMixin):
    def process_request(self, request):
        print("中介軟體2")
        # return HttpResponse("禁止你訪問")
    def process_response(self, request, response):
        print("中介軟體2返回")
        return response

class Row3(MiddlewareMixin):
    def process_request(self, request):
        print("中介軟體3")
    def process_response(self, request, response):
        print("中介軟體3返回")
        return response

settings.py

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',
    'middle.m.Row1',
    'middle.m.Row2',
    'middle.m.Row3',
]

views.py

def test(request):
    print("最終返回資訊")
    return HttpResponse("OK")

中介軟體裡面的引數request裡接受的資料和views裡接受的資料是一樣的,所以從裡面取值可以做相應的判斷處理,允不允許資料通過。

比如上面的示例:中介軟體row2,process_request,裡返回資料,則會在同級的process_response裡開始返回資料給客戶端了。

注意:這是在Django1.10版本才這樣的,之前版本:row2返回資料,會在最底部的response開始網上返回資料,這裡是row3。

所以中介軟體是對所有的請求做統一操作,比如資料校驗、黑名單過濾

2、process_view

如下:每個中介軟體類中中加入如下方法:

class Row1(MiddlewareMixin):
    def process_request(self, request):
        print("中介軟體1")
    def process_view(self, request, view_func, view_func_args, view_func_kwargs):
        # view_func 對應 views函式,view_func_args、kwargs 對應 views裡的引數、
        print("中介軟體1view")
    def process_response(self, request, response):
        print("中介軟體1返回")
        return response

請求順序如下:使用者請求 –> 每個中介軟體的request –> 到達urls路由匹配,匹配成功後 –> 折回每個中介軟體的view –> views –> 通過response返回

3、其他

def process_exception(self, request, exception):
    if isinstance(exception, ValueError):
        return HttpResponse("出現異常")
# 異常處理 views函式裡出錯了,執行這裡,如views裡 int('fgf')

views函式如果出現異常,返回會找exception方法,一級一級往上找,如果有處理返回,如果都沒有處理就直接返回報錯了。

process_template_response(self,request,response)
# 如果views中的函式返回的物件中,具有render方法,執行這個方法。

一、內容回顧

1、基本生命週期

2、URL

    /index/                 index
    /list/(\d+)             index
    /list/(\d+) name='li'   index
    /list/(\d+) include     index

3、views

# 所有內容的原生值
request.body        # 所有的post請求,都會放到body裡面傳過去
    request.POST        # 從request.body中提取
    request.GET         # 從request.body中提取
    request.FILES
    request.xxxx.getlist
# 請求頭內容
request.Meta
    request.method(POST,GET,PUT)
    request.path_info
    request.COOKIES
    ……

# 返回資料 #########
    return HttpResponse # 支援返回字串 和 bytes型別
    return render       # 渲染頁面
    return redirect     # 跳轉

    # 返回cookie ##
    response = HttpResponse('ok')
    response.set_cookie()
    return response
    # 把cookie,放到響應頭裡面,客戶端瀏覽器去響應頭裡面獲取。。所以也能設定響應頭內容
    response['name'] = 'fgf'

4、Model操作(原生Sql也可以)

# 表內容操作:
    models.TB.objects.create()
    obj = models.TB(..)
    obj.save()
    models.TB.objects.all()[7:10]  # 切片
    models.TB.objects.update()
    models.TB.objects.filter()
    models.TB.objects.delete
    models.TB.objects.values
    models.TB.objects.values_list
    models.TB.objects.get
    models.TB.objects.filter().update()
    models.TB.objects.filter().first()
    models.TB.objects.filter(**{}).count()
    models.TB.objects.filter(雙下劃線跨表)
    models.TB.objects.filter(id__gte=1)
    models.TB.objects.exclude(id__lt=1)

    models.TB.objects.filter(id_in=[1,2,3]) # id__in

    # 多對多
        obj.set
        obj.add([1,2,3])
        obj.add(1,2,3)
        obj.remove([1,2,3])
        obj.clear()


    models.TB.objects.all()
    [obj,obj]
    obj.fk.name
    models.TB.objects.all().order_by('')
    models.TB.objects.distinct()

    ## 跨表操作 ###########
    class A:
        name ...
        # 而A表操作B表,通過表名+‘_set’ --> b_set
    class B:
        caption ...
        fk = ForignKey(A)   # B表操作A表,通過 fk.

5、模板語言

# 基本操作
    def func(request):
        return render(request,'index.html',{'val':[1,2,3]})
    # index.html 裡
    <h1>{{ val.0 }}</h1>
# 繼承    
    extends "layout.html"
# include
    元件
# simpli_tag, filter

相關推薦

DjangoSessionCSRF中介軟體

大綱 二、session 1、session與cookie對比 2、session基本原理及流程 3、session伺服器操作(獲取值、設定值、清空值) 4、session通用配置(在配置檔案中) 5、session引擎配置(db、cache、fil

框架基礎:ajax設計方案--- 全局配置請求格式拓展和優化請求二進制類型瀏覽器錯誤搜集以及npm打包發布

rri seve win 最大 regexp isempty lee 出現問題 hub 距離上一次博客大概好多好多時間了,感覺再不搞點東西出來,感覺就廢了的感覺。這段時間回老家學習駕照,修養,然後7月底來上海求職(面了4家,拿了3家office),然後入職同程旅遊,項目趕進

LAMP+LNMP用戶認證域名跳轉與訪問日誌

Linux LAMP一、用戶認證有的網站需要加密特定的網頁,這是我們可以給網頁設置成輸入用戶名和密碼才能訪問,那麽具體的設置是 1、修改/usr/local/apache2.4/conf/extra/httpd-vhosts.conf文件我們先看下模板<VirtualHost *:80>Docum

Python之路---> 函數變量

() 參數名稱 外部 tro 標識 我們 (六) 函數聲明 局部變量   Python中的函數和數學上的函數定義是不一樣的,從數學的角度上來說函數的定義:給定一個數集A,假設其中的元素為x。現對A中的元素x施加對應法則f,記作f(x),得到另一數集B。假設B中的元素為y。則

從頭開始寫專案Makefile:引數傳遞條件判斷include

在多個Makefile巢狀呼叫時,有時我們需要傳遞一些引數給下一層Makefile。比如我們在頂層Makefile裡面定義的開啟除錯資訊變數DEBUG_SYMBOLS,我們希望在進入子目錄執行子Makefile時該變數仍然有效,這是需要將該變數傳遞給子Makefile,那怎麼傳遞呢?這裡有兩種方法:

Scala學習筆記:本地函式頭等函式佔位符和部分應用函式

本地函式 可以在方法內定義方法,這種方法叫本地函式,本地函式可以直接訪問父函式的引數 def parent(x: Int, y: Int): Unit ={ def child(y:Int) = y + 1 val z = child(y) println(s"x: $x, z

MySQL基礎——插入修改刪除操作

DML語言(資料操作語言): 插入:insert 修改:update 刪除:delete 一、插入語句 語法: INSERT INTO 表名(列名,... ) VALUES(值1,... ); 注意事項: 1、插入的值的型別要與列的型別一致或相容 INSER

(java)IO流物件集合Properties類序列化流反序列化流列印流

【Properties特點】 /* * 集合物件Properties類特點: * 1. 繼承Hashtable,實現Map介面 * 2. 該集合無泛型,鍵值都是字串 * 3. 鍵值可以儲存到集合中,也可以儲存到持久化的裝置如硬碟、U盤、光碟上

微信小程式學習筆記檔案上傳下載

(以上傳圖片為例) 後臺上傳介面Upload.php:(tp5) <?php namespace app\home\controller; use think\Controller; class Upload extends First { //上傳

MongoDB學習資料庫的備份還原匯入及匯出

        MongoDB官方提供了兩套資料匯入匯出工具,一般來說,進行整庫匯出匯入時使用mongodump和mongorestore,這一對組合操作的資料是BSON格式,進行大量dump和res

EF學習和使用顯式載入按需載入

這篇部落格接著說EF載入資料的方式,今天介紹剩下的兩種:顯式載入和按需載入。 顯式載入(Explicit Loading) 顯式載入和延遲載入非常類似,不同的是顯式載入要手動關閉EF的延

微信小程式開發教程配置——app.jsonpage.json詳解

全域性配置:app.json  微信小程式的全域性配置儲存在app.json檔案中。開發者通過使用app.json來配置頁面檔案(pages)的路徑、視窗(window)表現、設定網路超時時間值(networkTimeout)以及配置多個切換頁(tarBar)等。  首先看一

資料結構看書筆記--樹的定義抽象資料型別儲存結構

樹:樹(Tree)是n(n>=0)個結點的有限集。n=0時稱為空樹,在任意一顆非空樹中:(1)有且只有一個特定的稱之為根(Root)的結點;(2)當n>1時,其餘結點可分為m(m>0)個互不相交的有限集T1、T2、……、Tm,其中每一個集合本身又是一棵樹,

Glide原始碼分析,快取架構存取命中分析

分析Glide快取策略,我們還得從之前分析的Engine#load方法入手,這個方法中,展示了快取讀取的一些策略,我們繼續貼上這塊程式碼。 Engine#load public <R> LoadStatus load( Gli

Web前端效能優化減少DNS查詢避免重定向

一、減少DNS查詢 基礎知識 DNS(Domain Name System): 負責將域名URL轉化為伺服器主機IP。 DNS查詢流程:首先檢視瀏覽器快取是否存在,不存在則訪問本機DNS快取,再不存在則訪問本地DNS伺服器。所以DNS也是開銷,通常瀏覽器查詢一個

leetcode刷題路徑總和IIIIII

(一)112題題目地址:https://leetcode-cn.com/problems/path-sum/description/題目描述:給定一個二叉樹和一個目標和,判斷該樹中是否存在根節點到葉子節點的路徑,這條路徑上所有節點值相加等於目標和。解決方案:/** * De

Hibernate實現一對多多對一對映關聯關係

一對多、多對一這種關係在現實生活中很多,例如部門與員工的關係,學校裡班級與學生的關係... 那麼在具體的系統實現中如果i實現這種關聯關係呢?這裡以部門和員工的關係為例。 部門實體類 package test.hibernate.hbmOneToMany; import

Redis--- Redis過期策略記憶體淘汰機制訊息及事物

1、簡述 (1)關於Redis鍵的過期策略,首先要了解兩種時間的區別,生存時間和過期時間; 生存時間:一段時長,如30秒、6000毫秒,設定鍵的生存時間就是設定這個鍵可以存在多長時間,命令有兩個 expire(秒)、pexpire(毫秒)(可以參考 Redis(四)--- R

Selenium+JavaSelenium 強制等待顯式等待隱實等待

前言 在實際測試過程中,由於網速或效能方面的原因,開啟相應的網頁後或在網頁上做了相應的操作,網頁上的元素可能不會馬上加載出來,這個時候需要在定位元素前等待一下,等元素加載出來後再進行定位,根據實際使用選擇需要的等待方式。 一、強制等待  強制等待是利用time模組的sleep方法來實現,最簡單粗暴的

servlet學習Session

一、問題 一個使用者的不同請求處理如何共享資料? 二、解決 使用session技術 三、原理 session技術是依賴cookie技術的服務端的資料儲存技術。使用者第一訪問伺服器的時候,伺服器會建立一個session物件給使用者,並將session物件的JSESSIONID使用c