歡迎光臨
每天分享高質量文章

深入分析 Session 和 Cookie

點選上方“芋道原始碼”,選擇“置頂公眾號”

技術文章第一時間送達!

原始碼精品專欄

 

來源:http://t.cn/EvGZYz0

在Web發展史中,我們知道瀏覽器與伺服器間採用的是http協議,而這種協議是無狀態的,所以這就導致了伺服器無法知道是誰在瀏覽網頁,但很明顯,一些網頁需要知道使用者的狀態,例如登陸,購物車等。

所以為瞭解決這一問題,先後出現了四種技術,分別是隱藏表單域,URL重寫,cookie,session。而用的最多也是比較重要的就是cookie和session了。

Cookie

首先來瞭解cookie

是什麼

cookie是瀏覽器儲存在使用者電腦上的一小段文字,通俗的來講就是當一個使用者透過http訪問到伺服器時,伺服器會將一些Key/Value鍵值對傳回給客戶端瀏覽器,並給這些資料加上一些限制條件,在條件符合時這個使用者下次訪問這個伺服器時,資料透過請求頭又被完整地給帶回伺服器,伺服器根據這些資訊來判斷不同的使用者。

也就是說,cookie是伺服器傳給客戶端並儲存在客戶端的一段資訊,這個Cookie是有大小,數量限制的!!

Cookie的建立

當前Cookie有兩個版本,分別對應兩種設定響應頭:“Set-Cookie”和“Set-Cookie2”。在Servlet中並不支援Set-Cookie2,所以我們來看看Set-Cookie的屬性項:

屬性項 屬性項介紹
NAME=VALUE 鍵值對,可以設定儲存的Key/Value,這裡NAME不能和其他屬性項名字一樣
Expires 過期時間,在這個時間點後Cookie失效
Domain 生成Cookie域名
Path 該Cookie是在當前那個路徑下生成的
Secure 加密設定,設定他之後,只會在SSH連線時才會回傳該Cookie

現在,我們先來瞭解這些屬性項,其他的都說的很清楚了,我們來看看Domain有什麼用:

現在,我們假設這裡有兩個域名:

域名A:a.b.f.com.cn 域名B:c.d.f.com.cn

顯然,域名A和域名B都是f.com.cn的子域名

  • 如果我們在域名A中的Cookie的domain設定為f.com.cn,那麼f.com.cn及其子域名都可以獲取這個Cookie,即域名A和域名B都可以獲取這個Cookie

  • 如果域名A和域名B同時設定Cookie的doamin為f.com.cn,那麼將出現改寫的現象

  • 如果域名A沒有顯式設定Cookie的domain方法,那麼domain就為a.b.f.com.cn,不一樣的是,這時,域名A的子域名將無法獲取這個Cookie

綜上所述,我們知道了domain的用處,聰明的小夥伴已經發現了,單點登陸就是用這個原理實現的。

好的,現在瞭解完了Set-Cookie的屬性項,開始建立Cookie

Web伺服器透過傳送一個稱為Set-Cookie的http訊息來建立一個Cookie:

Set-Cookie: value; expires=date; path=path

這裡我們思考一個問題,當我們在伺服器建立多個Cookie時,這些Cookie最終是在一個Header項中還是以獨立的Header存在的呢?

int size = essay-headers.size();
for(int i = 0;i < size;i++){
   outputBuffer.sendHeader(essay-headers.getName(i),essay-headers.getValue(i));
}
我們可以看到,構建http傳回位元組流時是將Header中所有的項順序寫出,而沒有進行任何修改。所以可以想象在瀏覽器在接收http傳回的資料時是分別解析每一個Header項。

++ 接著,在客戶端進行儲存,如何儲存呢?這裡又要對Cookie進行進一步的瞭解。 ++

Cookie的分類

會話級別Cookie:

所謂會話級別Cookie,就是在瀏覽器關閉之後Cookie就會失效。

持久級別Cookie:

儲存在硬碟的Cookie,只要設定了過期時間就是硬碟級別Cookie。

好的,現在cookie儲存在了客戶端,當我們去請求一個URL時,瀏覽器會根據這個URL路徑將符合條件的Cookie放在請求頭中傳給伺服器。

Session

各位客官看到這裡實屬不易,?,但我們還是要想想,Cookie是有大小限制和數量限制的,並且越來越多的Cookie代表客戶端和伺服器的傳輸量增加,可不可以每次傳的時候不傳所有cookie值,而只傳一個唯一ID,透過這個ID直接在伺服器查詢使用者資訊呢?答案是有的,這就是我們的session。

理解Session:

Session是基於Cookie來工作的,同一個客戶端每次訪問伺服器時,只要當瀏覽器在第一次訪問伺服器時,伺服器設定一個id並儲存一些資訊(例如登陸就儲存使用者資訊,視具體情況),並把這個id透過Cookie存到客戶端,客戶端每次和伺服器互動時只傳這個id,就可以實現維持瀏覽器和伺服器的狀態,而這個ID通常是NAME為JSESSIONID的一個Cookie。

Session和Cookie

實際上,有四種方式讓Session正常工作

  1. 透過URL傳遞SessionID

  2. 透過Cookie傳遞SessionID

  3. 透過SSL傳遞SessionID

  4. 透過隱藏表單傳遞SessionID

第一種情況:

當瀏覽器不支援Cookie功能時,瀏覽器會將使用者的SessionCookieName(預設為JSESSIONID)重寫到使用者請求的URL引數中。格式:/path/Servlet;name=value;name2=value2?Name3=value3

第三種情況:

會根據javax.servlet.request.ssl_session屬性值設定SessionID。

註:如果客戶端支援Cookie,又透過URL重寫,Tomcat仍然會解析Cookie中的SessionID並改寫URL中的SessionID

工作原理

我們先看session工作的時序圖

一、建立Session

當客戶端訪問到伺服器,伺服器會為這個客戶端透過request.getSession()方法建立一個Session,如果當前SessionID還沒有對應的HttpSession物件,就建立一個新的,並新增到org.apache.catalina.Manager的sessions容器中儲存,這就做到了對狀態的保持。當然,這個SessionID是唯一的

二、session儲存

由圖可知,session物件已經儲存在了Manager類中,StandardManager作為實現類,透過requestedSessionId從StandardManager的sessions集合中取出StandardSession物件。

我們來看看StandardManager時如何對所有StandardSession物件進行生命週期管理

當Servlet容器關閉:

StandardManager將持久化沒過期的StandardSession物件(必須呼叫Servlet容器中的stop和start命令,不能直接kill)

當Servlet容器重啟時:

StandardManager初始化會重讀這個檔案,解析出所有session物件。

三、session的銷毀

這裡有一個誤區,也是我之前的錯誤理解,就是我將session的生命週期理解成一次會話,瀏覽器開啟就建立,瀏覽器關閉就銷毀,這樣理解是錯的!!

session的宣告週期是從建立到超時過期

也就是說,當session建立後,瀏覽器關閉,會話級別的Cookie被銷毀,如果沒有超過設定時間,該SessionID對應的session是沒有被銷毀的

檢查session失效

檢查每個Session是否失效是在Tomcat的一個後臺執行緒完成的(backgroundProcess()方法中);除了後臺行程檢驗session是否失效外,呼叫request.getSession()也會檢查該session是否過期,當然,呼叫這種方法如果過期的話又會重新建立一個新的session。

做個總結

相同點和不同點

相同點(有關係的地方)

  • Session和Cookie都是為了讓http協議又狀態而存在

  • Session透過Cookie工作,Cookie傳輸的SessionID讓Session知道這個客戶端到底是誰

不同點

Session將資訊儲存到伺服器,Cookie將資訊儲存在客戶端

工作流程

當瀏覽器第一次訪問伺服器時,伺服器建立Session並將SessionID透過Cookie帶給瀏覽器儲存在客戶端,同時伺服器根據業務邏輯儲存相應的客戶端資訊儲存在session中;客戶端再訪問時上傳Cookie,伺服器得到Cookie後獲取裡面的SessionID,來維持狀態。

參考:alibaba叢書《深入分析Java Web》




如果你對 Dubbo / Netty 等等原始碼與原理感興趣,歡迎加入我的知識星球一起交流。長按下方二維碼噢

目前在知識星球更新了《Dubbo 原始碼解析》目錄如下:

01. 除錯環境搭建
02. 專案結構一覽
03. 配置 Configuration
04. 核心流程一覽

05. 拓展機制 SPI

06. 執行緒池

07. 服務暴露 Export

08. 服務取用 Refer

09. 註冊中心 Registry

10. 動態編譯 Compile

11. 動態代理 Proxy

12. 服務呼叫 Invoke

13. 呼叫特性 

14. 過濾器 Filter

15. NIO 伺服器

16. P2P 伺服器

17. HTTP 伺服器

18. 序列化 Serialization

19. 叢集容錯 Cluster

20. 優雅停機

21. 日誌適配

22. 狀態檢查

23. 監控中心 Monitor

24. 管理中心 Admin

25. 運維命令 QOS

26. 鏈路追蹤 Tracing

… 一共 69+ 篇

目前在知識星球更新了《Netty 原始碼解析》目錄如下:

01. 除錯環境搭建
02. NIO 基礎
03. Netty 簡介
04. 啟動 Bootstrap

05. 事件輪詢 EventLoop

06. 通道管道 ChannelPipeline

07. 通道 Channel

08. 位元組緩衝區 ByteBuf

09. 通道處理器 ChannelHandler

10. 編解碼 Codec

11. 工具類 Util

… 一共 61+ 篇

目前在知識星球更新了《資料庫物體設計》目錄如下:


01. 商品模組
02. 交易模組
03. 營銷模組
04. 公用模組

… 一共 17+ 篇

原始碼不易↓↓↓

點贊支援老艿艿↓↓

贊(0)

分享創造快樂