cookie與session的區別以及在Django中的實現

linkfun發表於2020-03-01

cookie和session這兩個詞想必大家都不陌生,在HTTP互動中經常會涉及到這兩個概念。但是它們的作用到底是什麼?二者之間到底有什麼區別與聯絡呢?

什麼是cookie

cookie的定義

根據百度百科的定義:Cookie,型別為“小型文字檔案”,是某些網站為了辨別使用者身份,進行Session跟蹤而儲存在使用者本地終端上的資料(通常經過加密)。

用大白話說,就是:

  1. cookie是由伺服器(web應用)生成,儲存在客戶端(使用者瀏覽器)上的一段資料。
  2. 該資料用於進行session跟蹤。
  3. 瀏覽器在後續對同一網站的請求中,都必須帶上該cookie。

cookie的用途

因為HTTP是無狀態的協議,每一次請求之間都是完全獨立的。但為了實現web應用的互動,必須將同一個使用者的多次請求關聯起來,才能產生有意義的行為。

最典型的例子就是網購時新增商品到購物車,既然HTTP是無狀態協議,那麼伺服器怎麼知道商品應該新增到哪個使用者的購物車中呢?這就是cookie的作用了。

cookie使用場景1:使用者登入

使用者成功登入電商網站後,伺服器會生成一個登入憑據,並通過response header中的 Set-Cookie 欄位將其返回給使用者瀏覽器,通知其儲存該cookie資訊。

# 用於舉例,實際的cookie是經過加密的
Set-Cookie: uid=1
複製程式碼

瀏覽器一般預設會將該cookie儲存在使用者電腦某個目錄下的一個文字檔案中,後續對該網站發起的request header中,都會攜帶該cookie。

這樣伺服器在收到使用者請求的同時,也可以從請求攜帶的cookie中知道該請求是來自哪個使用者了。這樣就實現了使用者登入狀態的保持。

cookie使用場景2:購物車商品新增

使用者新增商品A到其購物車時,伺服器可以修改cookie欄位,將商品A的資訊追加到cookie中,這樣就實現了將商品A與使用者關聯到一起的行為。

Set-Cookie: uid=1;prod=A
複製程式碼

當使用者又新增了商品B到購物車中時,伺服器又會將商品B的資訊追加到cookie中。

Set-Cookie: uid=1;prod=A; prod=B
複製程式碼

等到使用者結賬的時候,伺服器就可以從請求攜帶的cookie中獲取到該使用者選擇的所有商品資訊。

什麼是session

我們常說的session,實際上有兩層概念。

首先是它的抽象概念:在使用者端與服務端之間一對一連續的互動,抽象為一個使用者會話,也就是session。比如從使用者登入到登出之間進行的一系列互動,都屬於同一個session。

其次是它的實際概念:為了實現使用者會話的保持,服務端通過建立session物件記錄了使用者登入後的各種資訊,比如使用者名稱,使用者的操作等等。

session是服務端上對一個請求使用者進行標識的唯一資訊,並將session的資訊通過cookie返回給使用者,這樣就實現了在使用者端和服務端對使用者會話的跟蹤。

cookie和session的區別與聯絡

  1. cookie是儲存在客戶端的,比如使用者電腦上,通常是儲存在一個文字中;而session是儲存在服務端的,通常儲存在資料庫或者快取中,也可以儲存在檔案系統中。

  2. cookie是HTTP協議中的概念,位於request header中,伺服器可以通過response header中的Set-Cookie欄位設定和修改返回給客戶端的cookie;而session是一個服務端層面的概念,不通的程式語言可能有不同的實現方式,其目的都是為了記錄保持使用者的狀態。

  3. 我們常說的session會話保持,實際上就是通過藉助cookie的特性,將服務端儲存的session狀態返回給客戶端,並在整個使用者會話過程中通過cookie進行持續的互動,進而實現抽象層面的session會話保持。

所以說cookie本身並沒有和session有必然的聯絡,它只是我們用來實現session跟蹤的一種常用手段。

Django中的session實現

Django是通過中介軟體的形式支援session的。在建立專案時預設就會在settings.py中配置好session中介軟體。

INSTALLED_APPS = [
    'django.contrib.sessions',
]

MIDDLEWARE = [
    'django.contrib.sessions.middleware.SessionMiddleware',  
]
複製程式碼

Django預設會將session資訊儲存在資料庫中,可以通過 manage.py migrate 生成session資訊表。如果對效能要求比較高,也可以將session資訊配置儲存到快取中。

mysql> desc django_session;
+--------------+-------------+------+-----+---------+-------+
| Field        | Type        | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
| session_key  | varchar(40) | NO   | PRI | NULL    |       |
| session_data | longtext    | NO   |     | NULL    |       |
| expire_date  | datetime(6) | NO   | MUL | NULL    |       |
+--------------+-------------+------+-----+---------+-------+
3 rows in set (0.01 sec)
複製程式碼

可以看到,django_session表中有3個欄位,其中:

session_data 實際對使用者資訊進行加密後生成的密文,可以解密;

session_key是根據session_data密文生成的一串隨機字串,本身並無實際意義,僅用於插入到cookie中返回給客戶端,並在使用者發起請求時對請求中攜帶的cookie與django_session表中的session_key進行匹配,查詢是否有匹配的session_data;

expire_date記錄了該session的過期時間。

實踐是檢驗真理的唯一標準

在發起登入請求後,服務端在response header中攜帶了Set-Cookie欄位,內容包含了csrftoken,以及本次會話的sessionid。

(cookie中的csrftoken機制請見 CSRF攻擊與Django防範)

Set-Cookie:  csrftoken=ATfjCEJZVaN9zTYnSHPzSRJhWfVfFlxntOYyu2f64j4mf9clzigVxsBIFN293uu3; expires=Sun, 28 Feb 2021 14:58:22 GMT; Max-Age=31449600; Path=/; SameSite=Lax
Set-Cookie:  sessionid=j0b9jsiiumwfjzmt9k3tw05aw7q5xk2n; expires=Sun, 15 Mar 2020 14:58:22 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=Lax
複製程式碼

cookie與session的區別以及在Django中的實現

在Django資料庫中,可以看到django_session表中也有對應的記錄

cookie與session的區別以及在Django中的實現

在登入後的下一次請求中,可以看到request header中的cookie中已經攜帶了服務端返回的sessionid

cookie與session的區別以及在Django中的實現

至此,通過服務端的session機制,並藉助HTTP協議的cookie機制,就實現了HTTP無狀態協議的會話保持,使用者也就可以進行愉快的頁面互動了。

相關文章