1. 程式人生 > >深入分析 Session 和 Cookie

深入分析 Session 和 Cookie

在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 = headers.size();
for(int i = 0;i < size;i++){
   outputBuffer.sendHeader(headers.getName(i),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,來維持狀態。