(day58)10、Cookie、Session、Token、Django中間件

[toc]html

1、Cookie

(一)由來

HTTP協議的無狀態特性致使每次的請求都是獨立的,即客戶端和服務器在某次會話中產生的數據不會被保存,所以產生了Cookie,用來保存客戶端的用戶狀態python

(二)什麼是Cookie

  1. Cookie具體是指服務器發送出來存儲在瀏覽器上的一組組鍵值對,下次訪問服務器時瀏覽器會自動攜帶這些信息,以便服務器經過Cookie提取有用信息從而判斷訪問者
  2. Cookie最大支持4096字節,保存在客戶端,安全性低

(三)Django中操做Cookie

(1)設置Cookie

  1. 普通算法

    • obj.set_cookie(key,value,...)
    • obj爲HttpResponse或render或redirect實例化的對象
  2. 加鹽數據庫

    • obj.set_signed_cookie(key,value,salt='加密鹽', max_age=None, ...)
    • obj爲HttpResponse或render或redirect實例化的對象
  • 參數
    1. key:鍵
    2. value:值
    3. max_age:超時時間,以秒爲單位,不能給IE設置cookie
    4. expires:超時時間,以秒爲單位,能夠給IE設置cookie
    5. path:cookie生效的路徑,/表示根路徑,能夠被任何URL頁面訪問
    6. domain:cookie生效的域名
    7. secure:布爾值,是否爲HTTPS傳輸
    8. httponly:布爾值,只能http協議傳輸,沒法被JavaScript獲取
# views.py
def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')

        if username == 'wick' and password == '123':
            next_url = request.GET.get('next')
            obj = redirect('/home/')
            if next_url:
                obj = redirect(next_url)
            obj.set_cookie('whoami', 'wick',max_age=30)
            return obj
    return render(request, 'login.html')

(2)獲取Cookie

  1. 獲取cookiedjango

    request.COOKIES.get('key')瀏覽器

  2. 獲取加鹽的cookie值安全

    request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)服務器

    • default: 默認值
    • salt: 加密鹽
    • max_age: 後臺控制過時時間
from functools import wraps
def login_auth(func):
    @wraps(func)
    def inner(request,*args, **kwargs):
        print(request.path_info)
        print(request.get_full_path())
        # 判斷當前用戶是否登陸
        if request.COOKIES.get('whoami'):
            res = func(request,*args, **kwargs)
            return res
        else:
            target_url = request.path_info
            return redirect(f'/login/?next={target_url}')
    return inner

(3)刪除Cookie

obj.delete_cookie('key'):刪除用戶瀏覽器上以前設置的cookie值cookie

@login_auth
def logout(request):
    obj = redirect('/login/')
    obj.delete_cookie('whoami')
    return obj

2、Session

(一)由來

  1. 解決Cookie的安全性和存儲限制問題
  2. 服務器生成並保存在服務器,能夠存儲超過4096字節的鍵值對(依賴於Cookie)

(二)Django中操做Session

  1. Django中Session存儲在django_session表中,第一次使用時需執行遷移命令
  2. Django中Session失效時間默認是14天
  3. 同一個瀏覽器生成的session都會保存在django_session表中同一條數據中

(1)設置Session

  • 設置Session時發生的事情:網絡

    1. Django內部調用算法生成隨機字符串
    2. 將隨機字符串和加密後的數據(以及失效時間)存儲到django_session表中
    3. 將隨機字符串返回給瀏覽器,瀏覽器中以sessionid爲鍵名保存爲字典形式
  • 設置Session值

    request.session['k1'] = 123
    request.session.setdefault('k1',123) # 存在則不設置
  • 設置Session和Cooike的超時時間

    request.session.set_expiry(value)
    1. 若是value是個整數,session會在些秒數後失效。
    2. 若是value是個datatime或timedelta,session就會在這個時間後失效。
    3. 若是value是0,用戶關閉瀏覽器session就會失效。
    4. 若是value是None,session會依賴全局session失效策略

(2)獲取Session

  • 獲取Session時發生的事情:

    1. Django內部會去請求頭中獲取cookie中的sessionid對應的隨機字符串
    2. 將該字符串與django_session表中存儲的隨機字符串進行比對
    3. 比對成功,會把對應的加密後的數據獲取出來,封裝到request.session中,不然爲空字典
  • 獲取Session

    # 1. Session中數據
    request.session['k1']
    request.session.get('k1',None)
    
    # 2. 會話session的key
    request.session.session_key
    
    # 3. 檢查會話session的key在數據庫中是否存在
    request.session.exists("session_key")
    
    # 4. 全部 鍵、值、鍵值對
    request.session.keys()
    request.session.values()
    request.session.items()
    request.session.iterkeys()
    request.session.itervalues()
    request.session.iteritems()

(3)刪除Session

  1. del request.session['k1']:刪除Session中數據
  2. request.session.delete():刪除當前會話的全部Session數據,數據庫中也會刪除
  3. request.session.flush() :刪除當前的會話數據並刪除會話的Cookie,數據庫中也會刪除
  4. request.session.clear_expired():將全部Session失效日期小於當前日期的數據刪除,(Django中有自動動刪除機制)

3、Django中間件

(一)什麼是中間件

  1. 中間件是一個用來處理Django的請求和響應的框架級別的鉤子,本質上是一個自定義類
  2. 能夠全局範圍內在視圖函數執行前和施行後作一些額外的操做,好比用戶登陸校驗、用戶訪問頻率校驗、用戶權限校驗
  3. Django中默認有七個中間件
# 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',
]

(二)自定義中間件

  1. 中間件能夠定義五個方法
  2. 方法中參數request:必需參數,和視圖函數中request同樣
  3. 方法中返回值爲None時, 繼續按照django定義的規則向後繼續執行
  4. 方法中返回值爲HttpResponse對象時, 則直接將該對象返回給用戶
# 在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.mymiddleware.mymidd.MyMidd1',  # 自定義中間件MD1
    'app01.mymiddleware.mymidd.MyMidd2'  # 自定義中間件MD2
]

(1)process_request

  1. 默認按照自上而下的順序執行MIDDLEWARE中每個中間件內部的process_request方法
  2. 返回HttpResponse對象:請求會馬上中止從當前中間件的process_response 方法原路返回
# app01/mymiddleware/mymidd.py

from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):
    def process_request(self, request):
        print("MD1裏面的 process_request")


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2裏面的 process_request")
        pass

(2) process_response

  1. 在視圖函數以後 ,默認按照自下而上的順序執行每一箇中間件中的process_response 方法
  2. 必須給方法傳入兩個形參,request和response
  3. 必須返回response:response是視圖函數返回的HttpResponse對象 ,瀏覽器才能接受HttpResponse對象
# app01/mymiddleware/mymidd.py

from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):
    def process_request(self, request):
        print("MD1裏面的 process_request")

    def process_response(self, request, response):
        print("MD1裏面的 process_response")
        return response


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2裏面的 process_request")
        pass

    def process_response(self, request, response):
        print("MD2裏面的 process_response")
        return response

(3) process_view

  1. 執行視圖函數以前觸發(路由匹配以後)
  2. 返回 HttpResponse對象 : 直接在中間件中掉頭,倒敘執行一個個process_response方法,最後返回給瀏覽器
  3. 該方法有四個參數
    1. request: HttpRequest對象
    2. view_func:Django即將使用的視圖函數對象
    3. view_args:將傳遞給視圖的位置參數的列表.
    4. view_kwargs是將傳遞給視圖的關鍵字參數的字典 (不包括request)
# app01/mymiddleware/mymidd.py
from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):
    def process_request(self, request):
        print("MD1裏面的 process_request")

    def process_response(self, request, response):
        print("MD1裏面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__)


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2裏面的 process_request")
        pass

    def process_response(self, request, response):
        print("MD2裏面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

(4) process_exception

  1. 視圖函數出現錯誤異常自動觸發,按照從下往上的順序執行
  2. 兩個參數 request 和 exception ( 視圖函數異常產生的Exception對象 )
  3. 返回 HttpResponse對象 : 調用模板和中間件中的process_response方法,並返回給瀏覽器 (不會再執行process_exception 方法)

(5) process_template_response

  1. 視圖函數執行完成後, 視圖函數返回的對象有一個render()方法時纔會執行,倒序執行
  2. 兩個參數 request 和 response ,即必須返回response

(6)中間件執行流程

  1. 請求到達中間件, 先按照正序執行每一箇中間件的process_request方法 ,執行完後匹配路由
  2. 再順序執行中間件中的process_view方法 ,執行完後執行視圖函數
  3. 若是視圖函數中有異常,會倒序執行process_exception
  4. 若是視圖函數中返回對象有一個render()方法,會倒序執行process_exception方法
  5. 不然,會倒序執行process_response 方法,將響應發給客戶端
  • 方法中有response參數的必須返回response(process_template_response、process_response )
  • 方法中沒有response參數的(process_view、process_exception、process_response )
    1. 返回none:正常執行
    2. 返回HttpResponse的:會從當前中間件的response方法依次倒序執行,最終發送給客戶端

以上,process_request、process_view默認爲順序,process_exception、process_exception、process_response 默認爲倒序

4、Token(擴展)

(一)由來

客戶端頻繁向服務端請求數據,服務端頻繁的去數據庫查詢用戶名和密碼並進行對比 ,對數據庫和服務器壓力大,所以產生了Token

(二)什麼是Token

  1. Token是服務端生成的一串字符串
  2. 第一次登陸時, 服務器生成一個Token返回給客戶端
  3. 再次請求時, 客戶端只需帶上這個Token前來請求數據便可,無需再次帶上用戶名和密碼

(三)目的

減輕服務器的壓力,減小頻繁的查詢數據庫,使服務器更加健壯

(四)使用方式

(1) 設備號/設備mac地址做爲Token

  1. 登陸時將 設備號/mac地址 做爲參數傳給服務端
  2. 服務端接受後,用變量Token接收並保存在數據庫,並將該Token設置到session中 ,下次請求只需比對變量Token
  • 優勢: 只要登陸一次之後一直可使用

  • 缺點: 客戶端須要帶設備號/mac地址做爲參數傳遞,並且服務器端還須要保存

  • 若服務器的Token超時後,將客戶端傳遞的Token向數據庫中查詢,同時並賦值給變量Token,如此,Token的超時又從新計時

(2) session值做爲Token

  1. 攜帶用戶名和密碼登錄
  2. 服務端接受後判斷,若是正確, 將本地獲取sessionID 做爲Token返回給客戶端
  • 好處:不用存儲數據
  • 缺點:session過時後要從新登陸

(五)一些問題和解決方案

  • 問題

    在網絡很差或者併發請求時會致使屢次重複提交數據

  • 解決方案( 將session和Token套用 )

    1. 能夠對session進行加鎖
    2. 把token放到session中,當後一個請求到來時,使用session中的token進行比對,當不一致時,即爲重複提交,不容許經過
相關文章
相關標籤/搜索