Cookie和Session詳解

http是一個無狀態的協議,對於客戶端的每次請求服務端在發送完響應後一般會把連接給斷開,下次發送請求又會建立一個新的連接,這樣就產生了服務端無法判斷客戶端身份的問題,Cookie和Session就是解決服務端和客戶端交互問題的兩種方式。
1.Cookie
1).概念
服務端在客戶端存放的文件,裏面記錄了一些客戶端相關的信息以便於下次請求的時候再帶回服務器去。爲什麼需要cookie?因爲http協議是無狀態的,沒法確定兩次請求是否來於同一客戶端,這時候需要客戶端在請求中攜帶一些特定的信息起到身份標識等作用;通過這些信息可以方便確定請求對應的客戶端,從而更好的與客戶端進行交互。
2).cookie的屬性
當前的cookie有兩個版本:Version0和Version1,它們有兩種設置響應頭的標識,分別是”Set-Cookie”和”Set-Cookie2”,這兩個版本的屬性有些不同,目前Servlet規範中不支持Set-Cookie2響應頭,但是在一些屬性確是可以設置到Set-Cookie1中的,比如Max-age等。下面兩個表詳細描述了Cookie的相關屬性。
Version 0屬性項設置

屬性項 屬性項介紹
Name=Value 鍵值對,設置需要保存的key/value,名字不能和其它屬性重複
Expires 過期時間(2017-11-12:09:21:23 23形式),在設置的時間點後,該Cookie會失效
Domain 生成該Cookie的域名,如domain=”www.baidu.com”
Path 該Cookie是在當前哪個路徑下生成的,如Path=”/”表示在根目錄下生成
Secure 如果設置了這個屬性,那麼只會在SSH連接時纔會回傳該Cookie

Version 1屬性項設置

屬性項 屬性項介紹
NAME=VALUE 與Version 0相同
Version Set-Cookie響應頭的爲0,Set-Cookie2響應頭對應的1
Comment 註釋項,用戶說明該Cookie有何用途
CommentURL 服務器爲該Cookie提供的URL註釋
Discard 是否在會話結束後丟棄該Cookie項
Domain 類似於Vesion 0
Max-age 最大失效時間,與Version 0不同的是,這裏設置的是多少秒之後失效
Path 類似於Vesion 0
Port 該Cookie可以在什麼端口回傳服務端,如果有多個端口,以逗號隔開,如”Port=80,81,8080”
Secure 類似於Version0

除了上面這些屬性,其實Cookie還有個HttpOnly屬性,如果這個屬性爲true,則表示該Cookie不在客戶端展示出來,僅可以通過服務端進行修改,加強了一些Cookie的安全性。
對於Max-Age屬性,需要詳細解釋下:
如果max-age屬性爲正數,則表示該cookie會在max-age秒之後自動失效。瀏覽器會將max-age爲正數的cookie持久化,即寫到對應的cookie文件中。無論客戶關閉了瀏覽器還是電腦,只要還在max-age秒之前,登錄網站時該cookie仍然有效。
如果max-age爲負數,則表示該cookie僅在本瀏覽器窗口以及本窗口打開的子窗口內有效,關閉窗口後該cookie即失效。max-age爲負數的Cookie,爲臨時性cookie,不會被持久化,不會被寫到cookie文件中。cookie信息保存在瀏覽器內存中,因此關閉瀏覽器該cookie就消失了。cookie默認的max-age值爲-1。
‍如果max-age爲0,則表示刪除該cookie。cookie機制沒有提供刪除cookie的方法,因此通過設置該cookie即時失效實現刪除cookie的效果。失效的Cookie會被瀏覽器從cookie文件或者內存中刪除。
如果不設置expires或者max-age這個cookie默認是Session的,也就是關閉瀏覽器該cookie就消失了。
上面講到Version 1版本的Cookie還沒有被支持,但是Verson 0 版本的Cookie中又可以引用一些前者的屬性,下面通過javax.servlet.http.Cookie的源碼來看下具體都有哪些值:

  //
    // The value of the cookie itself.
    //

    private String name;    // NAME= ... "$Name" style is reserved
    private String value;   // value of NAME

    //
    // Attributes encoded in the header's cookie fields.
    //

    private String comment; // ;Comment=VALUE ... describes cookie's use
                // ;Discard ... implied by maxAge < 0
    private String domain;  // ;Domain=VALUE ... domain that sees cookie
    private int maxAge = -1;    // ;Max-Age=VALUE ... cookies auto-expire
    private String path;    // ;Path=VALUE ... URLs that see the cookie
    private boolean secure; // ;Secure ... e.g. use SSL
    private int version = 0;    // ;Version=1 ... means RFC 2109++ style
    private boolean isHttpOnly = false;

3).在Servlet中操作Cookie
通過request.getCookies()方法可以拿到請求帶上來的所有Cookie,通過名字可以拿到指定的Cookie。

  Cookie[] cookies = req.getCookies();
        for(Cookie cookie : cookies){
            System.out.println(cookie.getValue());
            if(cookie.getName().equals("name")){
                System.out.println(cookie.getValue());
            }
        }

通過response.addCookie()方法可以增加一個新的Cookie

  Cookie cookie = new Cookie("userName", "test");
        cookie.setMaxAge(1000);
        cookie.setPath("/");
        resp.addCookie(cookie);

Servlet規範中沒有顯示提供刪除一個Cookie的方法,只需要把Cookie的maxAge設爲0,就可以令Cookie失效達到刪除的目的了。比如說想刪除上面新添加的那個Cookie:

   Cookie cookie = new Cookie("userName", "test");
        cookie.setMaxAge(0);
        cookie.setPath("/");
        resp.addCookie(cookie);

這裏需要注意的是隻有name/value、path、domain都相同的才能算是同一個Cookie,把這樣Cookie的maxAge設置爲0才能起到刪除的作用,如果有一個屬性不一致,比如path不一樣,則操作的不是同一Cookie。可以通過如下方式來刪除所有的Cookie:

 Cookie[] cookies = req.getCookies();
        for (Cookie cookie : cookies) {
            cookie.setMaxAge(0);
            resp.addCookie(cookie);
        }

4).Cookie工作機制
當我們通過Response.addCookie()方法增加了一個Cookie之後,它是如何添加到響應頭中帶到客戶端的。下面以tomcat爲例來了解下相關流程。下圖是Tomcat如何創建Set-Cookie響應頭的。
這裏寫圖片描述
簡要的來說就是將Cookie中的各個屬性拼成一個字符串,字符串的格式形如:userName=”test”;Version=”1”;domain=”www.baidu.com”;Max-Age=1000然後將這個字符串以Set-Cookie的名稱加入MimeHeaders中去。有幾點需要注意的地方:

  • 所創建的Cookie的NAME不能和Set-Cookie和Set-Cookie2中屬性值名稱一樣,否則會拋異常。
  • 所創建Cookie的NAME和VALUE不能設置成非ASCII字符,如果要使用中文,可以通過URLEncoder,否則會拋異常
  • 當NAME和VALUE的值出現一些TOKEN字符(如”\”,”,”等),Cookie Version會自動設爲1
  • 當在該Cookie的屬性項中出現Version爲1的屬性項時,構建HTTP響應頭同樣會將Version設置爲1

如果我們通過response.addCookie()來添加多個Cookie,那麼這多個都會被以Set-Cookie的名稱加入到Headers中去,這樣就有一個問題,在返回客戶端的時候,這些具有相同名稱的頭部是否會合併成同一個頭部返回?從返回代碼中來看,並不會出現這種情況。而是各個Header順序寫入的,客戶端也是順序解析這些header的,也就是說可能需要解析多個名稱爲Set-Cookie的頭部。
上面講的是服務端如何創建並傳輸Cookie的,對於客戶端,當我們請求某個URL路徑時,瀏覽器會根據這個路徑取到符合條件的Cookie放在請求頭中傳輸給服務端。 值得注意的雖然在Http中Cookie僅僅是一個頭部,沒有加上什麼限制,但因爲最終的存儲還是在客戶端中,所以大部分的客戶端還是會限制Cookie的數量和大小,比如Chrome就限制每個域名最多50個Cookie,而FireFox不僅限制每個域名最多50個cookie,並且還限制了總
長度不超過4097字節。

2.Session
1).概述
前面介紹了Cookie可以讓服務端程序跟蹤客戶端的每個訪問,但每次客戶端的訪問都必須傳回這些Cookie,如果Cookie很多,則無疑增加了客戶端和服務端之間的數據傳輸量,另外因爲Cookie是暴露給客戶端的,客戶端可以對其進行修改,所以還存在一定安全性問題。Session的出現正是爲了解決這個問題。
同一個客戶端每次和服務端交互時,不需要每次都傳回所有的Cookie值,而只需要傳回一個ID,這個ID是客戶端第一次範圍服務器時候生成的,而且每個客戶端都是唯一的,這樣每個客戶端都有個唯一的ID,每次訪問的時候只需要回傳這個ID,然後通過這個ID可以在服務端找到包含客戶端信息的文件,也就可以記錄客戶端的訪問信息了。
客戶端訪問tomcat服務器HttpServletRequest的getSession(true)的時候創建,tomcat的ManagerBase類提供創建sessionid的方法:隨機數+時間+jvmid;創建之後就會填充到Cookie中去。
2).工作模式
Session是基於Cookie來工作的,總的來說一共有三種模式可以使得客戶端的SessionId傳遞到服務端。
1) 基於URL Path Parameter 默認支持
2) 基於Cookie,如果沒有修改Context容器的cookie標識,那麼也是默認支持的
3) 至於SSL,默認不支持,只有connector.getAttribute(“SSL Enable”)爲true時才支持
第一種情況下,當瀏覽器不支持Cookie功能時,瀏覽器會將用戶的SessionCookieName重寫到用戶請求的URL參數中,它的傳遞格式如/path/servlet;name=value1;name=value2?name=value3,這裏servlet後面的就是path Parameter,服務器會從這個path Parameter中取到用戶配置的SessionCookieName,這裏的SessionCookieName默認就是JSESSIONID,如果在web.xml中配置了session-config,那麼其下的cookie-config下的name屬性就是這個SessionCookieName的值。 如果客戶端也支持Cookie,那麼服務端仍然會解析Cookie中的SessionId值,並覆蓋url中的.如果是第三種情況,那麼服務端會根據java.servlet.request.ssl_session屬性值來設置sessionId。
下面以Tomcat爲例來具體說明下Session的管理流程(每種Servlet容器管理Session的方式都是自定義的)。首先StandarManger類負責Servlet容器中所有的Session對象生命週期的管理。當Servlet容器關閉或重啓時,StandarManger會調用unload方法將所有未過期的Session對象持久化到一個以”SESSION.ser”爲文件名的文件中,到Servlet重啓時,也就是StandarManger初始化時,它會重新讀取這個文件,按照下面的狀態圖重新恢復。
這裏寫圖片描述
另外StandarManger的sessions集合中的StandarManger對象並不是永久保存的,否則Servlet容器的內存將會被很快耗光,所以必須給每個Session定義一個有效時間,超過這個時間則Session對象將會被清除.在Tomcat中這個有效時間是60s(由maxInactiveInterval屬性控制),超過60s該Session將會過期.檢查每個Session的有效性是在Tomcat一個後臺線程中完成的,過期Session的狀態如圖所示。
這裏寫圖片描述
除了後臺線程檢查Session是否過期之外,當調用request.getSession()方法時也會檢查Session是否過期.值得注意的是,request.getSession()方法永遠都會返回一個正在生效的Session,如果上次改SessionId對應的Session已經失效了,那麼就會重新生成一個新Session對象,這時候可能出現在原先Session中設置的Attribute已失效的情況;如果只想看下客戶端關聯的Session對象是否已存在,但就算不存在或過期也不想重建一個新的,可以通過request.getSession(false)方法來查看,如果沒有關聯的Session對象,則會返回null。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章