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

深入分析 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)

分享創造快樂