目錄
會話(Session)跟蹤是Web程序中常用的技術,用來跟蹤用戶的整個會話。常用的會話跟蹤技術是Cookie與Session。
Cookie通過在客戶端記錄信息確定用戶身份,Session通過在服務器端記錄信息確定用戶身份。
一、關於Cookie
cookie的基本概念
cookie是某些網站爲了辨別用戶身份,進行Session跟蹤而儲存在用戶本地終端上的數據(通常經過加密),由用戶客戶端計算機暫時或永久保存的信息。
Cookie的類型
Cookie總時由用戶客戶端進行保存的(一般是瀏覽器),按其存儲位置可分爲:內存式Cookie和硬盤式Cookie。
內存式Cookie存儲在內存中,瀏覽器關閉後就會消失,由於其存儲時間較短,因此也被稱爲非持久Cookie或會話Cookie。
硬盤式Cookie保存在硬盤中,其不會隨瀏覽器的關閉而消失,除非用戶手工清理或到了過期時間。由於硬盤式Cookie存儲時間是長期的,因此也被稱爲持久Cookie。
1、cookie的工作原理
Cookie定義了一些HTTP請求頭和HTTP響應頭,通過這些HTTP頭信息使服務器可以與客戶進行狀態交互。
客戶端請求服務器後,如果服務器需要記錄用戶狀態,服務器會在響應信息中包含一個Set-Cookie的響應頭,客戶端會根據這個響應頭存儲Cookie信息。再次請求服務器時,客戶端會在請求信息中包含一個Cookie請求頭,而服務器會根據這個請求頭進行用戶身份、狀態等較驗。
下面是一個實現Cookie機制的,簡單的HTTP請求過程:
1. 客戶端請求服務器
客戶端請求IT筆錄網站首頁,請求頭如下:
GET / HTTP/1.0
HOST: itbilu.com
2. 服務器響應請求
Cookie是一種key=value形式的字符串,服務器需要記錄這個客戶端請求的狀態,因此在響應頭中包一個Set-Cookie字段。響應頭如下:
HTTP/1.0 200 OK
Set-Cookie: UserID=itbilu; Max-Age=3600; Version=1
Content-type: text/html
……
3. 再次請求時,客戶端請求中會包含一個Cookie請求頭
客戶端會對服務器響應的Set-Cookie頭信息進行存儲。再次請求時,將會在請求頭中包含服務器響應的Cookie信息。請求頭如下
GET / HTTP/1.0
HOST: itbilu.com
Cookie: UserID=itbilu
2、cookie的屬性設置
除了name與value之外,Cookie還具有其他幾個常用的屬性。每個屬性對應一個getter方法與一個setter方法。Cookie類的所有屬性如表下所示。
Java中把Cookie封裝成了javax.servlet.http.Cookie類。每個Cookie都是該Cookie類的對象。服務器通過操作Cookie類對象對客戶端Cookie進行操作。
通過request.getCookie()獲取客戶端提交的所有Cookie(以Cookie[]數組形式返回),通過response.addCookie(Cookiecookie)向客戶端設置Cookie。
Cookie對象使用key-value屬性對的形式保存用戶狀態,一個Cookie對象保存一個屬性對,一個request或者response同時使用多個Cookie。因爲Cookie類位於包javax.servlet.http.*下面,所以JSP中不需要import該類。
(1)Unicode編碼:保存中文
中文與英文字符不同,中文屬於Unicode字符,在內存中佔4個字符,而英文屬於ASCII字符,內存中只佔2個字節。Cookie中使用Unicode字符時需要對Unicode字符進行編碼,否則會亂碼。
提示:Cookie中保存中文只能編碼。一般使用UTF-8編碼即可。不推薦使用GBK等中文編碼,因爲瀏覽器不一定支持,而且JavaScript也不支持GBK編碼。
(2)設置Cookie的有效期
Cookie的maxAge決定着Cookie的有效期,單位爲秒(Second)。Cookie中通過getMaxAge()方法與setMaxAge(int maxAge)方法來讀寫maxAge屬性。
如果maxAge屬性爲正數:則表示該Cookie會在maxAge秒之後自動失效。瀏覽器會將maxAge爲正數的Cookie持久化,即寫到對應的Cookie文件中。無論客戶關閉了瀏覽器還是電腦,只要還在maxAge秒之前,登錄網站時該Cookie仍然有效。下面代碼中的Cookie信息將永遠有效。
Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie
cookie.setMaxAge(Integer.MAX_VALUE); // 設置生命週期爲MAX_VALUE
response.addCookie(cookie); // 輸出到客戶端
如果maxAge爲負數:則表示該Cookie僅在本瀏覽器窗口以及本窗口打開的子窗口內有效,關閉窗口後該Cookie即失效。maxAge爲負數的Cookie,爲臨時性Cookie,不會被持久化,不會被寫到Cookie文件中。Cookie信息保存在瀏覽器內存中,因此關閉瀏覽器該Cookie就消失了。Cookie默認的maxAge值爲–1。
如果maxAge爲0:則表示刪除該Cookie。Cookie機制沒有提供刪除Cookie的方法,因此通過設置該Cookie即時失效實現刪除Cookie的效果。失效的Cookie會被瀏覽器從Cookie文件或者內存中刪除,例如:
Cookie cookie = new Cookie("username","helloweenvsfei"); // 新建Cookie
cookie.setMaxAge(0); // 設置生命週期爲0,不能爲負數
response.addCookie(cookie); // 必須執行這一句
response對象提供的Cookie操作方法只有一個添加操作add(Cookie cookie)。
要想修改Cookie只能使用一個同名的Cookie來覆蓋原來的Cookie,達到修改的目的。刪除時只需要把maxAge修改爲0即可。
注意:從客戶端讀取Cookie時,包括maxAge在內的其他屬性都是不可讀的,也不會被提交。瀏覽器提交Cookie時只會提交name與value屬性。maxAge屬性只被瀏覽器用來判斷Cookie是否過期。
(3)修改和刪除cookie
Cookie並不提供修改、刪除操作。如果要修改某個Cookie,只需要新建一個同名的Cookie,添加到response中覆蓋原來的Cookie。
如果要刪除某個Cookie,只需要新建一個同名的Cookie,並將maxAge設置爲0,並添加到response中覆蓋原來的Cookie。注意是0而不是負數。負數代表其他的意義。
注意:修改、刪除Cookie時,新建的Cookie除value、maxAge之外的所有屬性,例如name、path、domain等,都要與原Cookie完全一樣。否則,瀏覽器將視爲兩個不同的Cookie不予覆蓋,導致修改、刪除失敗。
(4)設置cookie的域名
Cookie是不可跨域名的。域名www.google.com頒發的Cookie不會被提交到域名www.baidu.com去。這是由Cookie的隱私安全機制決定的。隱私安全機制能夠禁止網站非法獲取其他網站的Cookie。
正常情況下,同一個一級域名下的兩個二級域名如www.helloweenvsfei.com和images.helloweenvsfei.com也不能交互使用Cookie,因爲二者的域名並不嚴格相同。如果想所有helloweenvsfei.com名下的二級域名都可以使用該Cookie,需要設置Cookie的domain參數,例如:
Cookie cookie = new Cookie("time","20080808"); // 新建Cookie
cookie.setDomain(".helloweenvsfei.com"); // 設置域名
cookie.setPath("/"); // 設置路徑
cookie.setMaxAge(Integer.MAX_VALUE); // 設置有效期
response.addCookie(cookie); // 輸出到客戶端
讀者可以修改本機C:\WINDOWS\system32\drivers\etc下的hosts文件來配置多個臨時域名,然後使用setCookie.jsp程序來設置跨域名Cookie驗證domain屬性。
注意:domain參數必須以點(".")開始。另外,name相同但domain不同的兩個Cookie是兩個不同的Cookie。如果想要兩個域名完全不同的網站共有Cookie,可以生成兩個Cookie,domain屬性分別爲兩個域名,輸出到客戶端。
(5)設置Cookie的路徑
domain屬性決定運行訪問Cookie的域名,而path屬性決定允許訪問Cookie的路徑(ContextPath)。例如,如果只允許/session/下的程序使用Cookie,可以這麼寫:
Cookie cookie = new Cookie("time","20080808"); // 新建Cookie
cookie.setPath("/session/"); // 設置路徑
response.addCookie(cookie); // 輸出到客戶端
設置爲“/”時允許所有路徑使用Cookie。path屬性需要使用符號“/”結尾。name相同但domain相同的兩個Cookie也是兩個不同的Cookie。
注意:頁面只能獲取它屬於的Path的Cookie。例如/session/test/a.jsp不能獲取到路徑爲/session/abc/的Cookie。使用時一定要注意。
(6)設置cookie的安全屬性
HTTP協議不僅是無狀態的,而且是不安全的。使用HTTP協議的數據不經過任何加密就直接在網絡上傳播,有被截獲的可能。使用HTTP協議傳輸很機密的內容是一種隱患。如果不希望Cookie在HTTP等非安全協議中傳輸,可以設置Cookie的secure屬性爲true。瀏覽器只會在HTTPS和SSL等安全協議中傳輸此類Cookie。下面的代碼設置secure屬性爲true:
Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie
cookie.setSecure(true); // 設置安全屬性
response.addCookie(cookie); // 輸出到客戶端
提示:secure屬性並不能對Cookie內容加密,因而不能保證絕對的安全性。如果需要高安全性,需要在程序中對Cookie內容加密、解密,以防泄密。
二、關於session
Session是另一種記錄客戶狀態的機制,不同的是Cookie保存在客戶端瀏覽器中,而Session保存在服務器上。客戶端瀏覽器訪問服務器的時候,服務器把客戶端信息以某種形式記錄在服務器上。這就是Session。客戶端瀏覽器再次訪問時只需要從該Session中查找該客戶的狀態就可以了。
如果說Cookie機制是通過檢查客戶身上的“通行證”來確定客戶身份的話,那麼Session機制就是通過檢查服務器上的“客戶明細表”來確認客戶身份。Session相當於程序在服務器上建立的一份客戶檔案,客戶來訪的時候只需要查詢客戶檔案表就可以了。
注意:Session是服務器端使用的一種記錄客戶端狀態的機制,使用上比Cookie簡單一些,相應的也增加了服務器的存儲壓力。
1、session的工作原理
當程序需要爲某個客戶端的請求創建一個session的時候,服務器首先檢查這個客戶端的請求裏是否已包含了一個session標識,稱爲session id,如果已包含一個session id則說明以前已經爲此客戶端創建過session,服務器就按照session id把這個session檢索出來使用(如果檢索不到,可能會新建一個),如果客戶端請求不包含session id,則爲此客戶端創建一個session並且生成一個與此session相關聯的session id,session id的值應該是一個既不會重複,又不容易被找到規律以仿造的字符串,這個session id將被在本次響應中返回給客戶端保存。
如圖,session工作原理圖示:
保存session id的方式
(1)使用Cookie進行保存
保存session id的方式可以採用cookie,這樣在交互過程中瀏覽器可以自動的按照規則把這個標識發揮給服務器。一般這個cookie的名字都是類似於SEEESIONID。然而,比如weblogic對於web應用程序生成的cookie,JSESSIONID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是JSESSIONID。
(2)URL重寫
由於cookie可以被人爲的禁止,必須有其他機制以便在cookie被禁止時仍然能夠把session id傳遞迴服務器。經常被使用的一種技術叫做URL重寫,就是把session id直接附加在URL路徑的後面。
附加方式也有兩種,一種是作爲URL路徑的附加信息,表現形式爲:
http://...../xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
另一種是作爲查詢字符串附加在URL後面,表現形式爲:
http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
這兩種方式對於用戶來說是沒有區別的,只是服務器在解析的時候處理的方式不同,採用第一種方式也有利於把session id的信息和正常程序參數區分開來。爲了在整個交互過程中始終保持狀態,就必須在每個客戶端可能請求的路徑後面都包含這個session id。
注意:TOMCAT判斷客戶端瀏覽器是否支持Cookie的依據是請求中是否含有Cookie。儘管客戶端可能會支持Cookie,但是由於第一次請求時不會攜帶任何Cookie(因爲並無任何Cookie可以攜帶),URL地址重寫後的地址中仍然會帶有jsessionid。當第二次訪問時服務器已經在瀏覽器中寫入Cookie了,因此URL地址重寫後的地址中就不會帶有jsessionid了。
(3)表單隱藏字段
表單隱藏就是服務器會自動修改表單,添加一個隱藏字段,以便在表單提交時能夠把session id傳遞迴服務器。比如下面的表單:
<form name="testform" action="/xxx">
<input type="text">
</form>
在被傳遞給客戶端之前將被改寫成:
<form name="testform" action="/xxx">
<input type="hidden" name="jsessionid"
value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>
這種技術現在已較少應用,實際上這種技術可以簡單的用對action應用URL重寫來代替。
2、Session的常用方法
Session中包括各種方法,使用起來要比Cookie方便得多。HttpSession的常用方法如表所示。
Session對應的類爲javax.servlet.http.HttpSession類。每個來訪者對應一個Session對象,所有該客戶的狀態信息都保存在這個Session對象裏。Session對象是在客戶端第一次請求服務器的時候創建的。Session也是一種key-value的屬性對,通過getAttribute(String key)和setAttribute(String key,Objectvalue)方法讀寫客戶狀態信息。Servlet裏通過request.getSession()方法獲取該客戶的Session,例如:
HttpSession session = request.getSession(); // 獲取Session對象
session.setAttribute("loginTime", new Date()); // 設置Session中的屬性
out.println("登錄時間爲:" +(Date)session.getAttribute("loginTime")); // 獲取Session屬性
request還可以使用getSession(boolean create)來獲取Session。區別是如果該客戶的Session不存在,request.getSession()方法會返回null,而getSession(true)會先創建Session再將Session返回。
當多個客戶端執行程序時,服務器會保存多個客戶端的Session。獲取Session的時候也不需要聲明獲取誰的Session。Session機制決定了當前客戶只會獲取到自己的Session,而不會獲取到別人的Session。各客戶的Session也彼此獨立,互不可見。
Session的生命週期和有效期設置
Session保存在服務器端。爲了獲得更高的存取速度,服務器一般把Session放在內存裏。每個用戶都會有一個獨立的Session。如果Session內容過於複雜,當大量客戶訪問服務器時可能會導致內存溢出。因此,Session裏的信息應該儘量精簡。
Session在用戶第一次訪問服務器的時候自動創建。需要注意只有訪問JSP、Servlet等程序時纔會創建Session,只訪問HTML、IMAGE等靜態資源並不會創建Session。如果尚未生成Session,也可以使用request.getSession(true)強制生成Session。
Session生成後,只要用戶繼續訪問,服務器就會更新Session的最後訪問時間,並維護該Session。用戶每訪問服務器一次,無論是否讀寫Session,服務器都認爲該用戶的Session“活躍(active)”了一次。
由於會有越來越多的用戶訪問服務器,因此Session也會越來越多。爲防止內存溢出,服務器會把長時間內沒有活躍的Session從內存刪除。這個時間就是Session的超時時間。如果超過了超時時間沒訪問過服務器,Session就自動失效了。
Session的超時時間爲maxInactiveInterval屬性,可以通過對應的getMaxInactiveInterval()獲取,通過setMaxInactiveInterval(long interval)修改。另外,通過調用Session的invalidate()方法可以使Session失效。Tomcat中Session的默認超時時間爲20分鐘。通過setMaxInactiveInterval(int seconds)修改超時時間。Session的超時時間也可以在web.xml中修改。例如修改爲60分鐘:
<session-config>
<session-timeout>60</session-timeout> <!-- 單位:分鐘 -->
</session-config>
注意:<session-timeout>參數的單位爲分鐘,而setMaxInactiveInterval(int s)單位爲秒。
三、session和cookie的區別與聯繫
兩者的區別:在安全和性能方面考慮
1、cookie數據存放在客戶端,session數據放在服務器上。
2、cookie不是很安全,別人可以分析存放在本地的cookie並進行cookie欺騙,考慮到安全應當使用session。
3、session會在一定時間內保存在服務器上,當訪問增多,會比較佔用你服務器的性能,考慮性能應當使用cookie。
4、不同瀏覽器對cookie的數據大小限制不同,個數限制也不相同。單個cookie保存的數據不能超過4K,很多瀏覽器都限制一個站點最多保存20個cookie。
5、可以考慮將登陸信息等重要信息存放爲session,不重要的信息可以放在cookie中。
兩者的聯繫:
1、都是用來記錄用戶的信息,以便讓服務器分辨不同的用戶。
2、可以搭配使用,但都有自己的使用侷限,要考慮到安全和性能的問題。