會話(Session)跟蹤是Web程式中常用的技術,用來跟蹤使用者的整個會話。常用的會話跟蹤技術是Cookie與Session。Cookie通過在用戶端記錄資訊确定使用者身份,Session通過在伺服器端記錄資訊确定使用者身份。
1.1 Cookie機制
在程式中,會話跟蹤是很重要的事情。理論上,一個使用者的所有請求操作都應該屬于同一個會話,而另一個使用者的所有請求操作則應該屬于另一個會話,二者不能混淆。例如,使用者A在超市購買的任何商品都應該放在A的購物車内,不論是使用者A什麼時間購買的,這都是屬于同一個會話的,不能放入使用者B或使用者C的購物車内,這不屬于同一個會話。
而Web應用程式是使用HTTP協定傳輸資料的。HTTP協定是無狀态的協定。一旦資料交換完畢,用戶端與伺服器端的連接配接就會關閉,再次交換資料需要建立新的連接配接。這就意味着伺服器無法從連接配接上跟蹤會話。即使用者A購買了一件商品放入購物車内,當再次購買商品時伺服器已經無法判斷該購買行為是屬于使用者A的會話還是使用者B的會話了。要跟蹤該會話,必須引入一種機制。
Cookie就是這樣的一種機制。它可以彌補HTTP協定無狀态的不足。在Session出現之前,基本上所有的網站都采用Cookie來跟蹤會話。
1.1.1 什麼是Cookie
Cookie意為“甜餅”,是由W3C組織提出,最早由Netscape社群發展的一種機制。目前Cookie已經成為标準,所有的主流浏覽器如IE、Netscape、Firefox、Opera等都支援Cookie。
由于HTTP是一種無狀态的協定,伺服器單從網絡連接配接上無從知道客戶身份。怎麼辦呢?就給用戶端們頒發一個通行證吧,每人一個,無論誰通路都必須攜帶自己通行證。這樣伺服器就能從通行證上确認客戶身份了。這就是Cookie的工作原理。
Cookie實際上是一小段的文本資訊。用戶端請求伺服器,如果伺服器需要記錄該使用者狀态,就使用response向用戶端浏覽器頒發一個Cookie。用戶端浏覽器會把Cookie儲存起來。當浏覽器再請求該網站時,浏覽器把請求的網址連同該Cookie一同送出給伺服器。伺服器檢查該Cookie,以此來辨認使用者狀态。伺服器還可以根據需要修改Cookie的内容。

檢視某個網站頒發的Cookie很簡單。在浏覽器位址欄輸入javascript:alert (document. cookie)就可以了(需要有網才能檢視)。
注意:Cookie功能需要浏覽器的支援。
如果浏覽器不支援Cookie(如大部分手機中的浏覽器)或者把Cookie禁用了,Cookie功能就會失效。
不同的浏覽器采用不同的方式儲存Cookie。
IE浏覽器會在“C:\Documents and Settings\你的使用者名\Cookies”檔案夾下以文本檔案形式儲存,一個文本檔案儲存一個Cookie。
1.1.2 記錄使用者通路次數
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.1.3 Unicode編碼:儲存中文
中文與英文字元不同,中文屬于Unicode字元,在記憶體中占4個字元,而英文屬于ASCII字元,記憶體中隻占2個位元組。Cookie中使用Unicode字元時需要對Unicode字元進行編碼,否則會亂碼。
提示:Cookie中儲存中文隻能編碼。一般使用UTF-8編碼即可。不推薦使用GBK等中文編碼,因為浏覽器不一定支援,而且JavaScript也不支援GBK編碼。
1.1.5 BASE64編碼:儲存二進制圖檔
Cookie不僅可以使用ASCII字元與Unicode字元,還可以使用二進制資料。例如在Cookie中使用數字證書,提供安全度。使用二進制資料時也需要進行編碼。
%注意:本程式僅用于展示Cookie中可以存儲二進制内容,并不實用。由于浏覽器每次請求伺服器都會攜帶Cookie,是以Cookie内容不宜過多,否則影響速度。Cookie的内容應該少而精。
1.1.6 設定Cookie的所有屬性
除了name與value之外,Cookie還具有其他幾個常用的屬性。每個屬性對應一個getter方法與一個setter方法。Cookie類的所有屬性如表1.1所示。
表1.1 Cookie常用屬性
屬 性 名 | 描 述 |
String name | 該Cookie的名稱。Cookie一旦建立,名稱便不可更改 |
Object value | 該Cookie的值。如果值為Unicode字元,需要為字元編碼。如果值為二進制資料,則需要使用BASE64編碼 |
int maxAge | 該Cookie失效的時間,機關秒。如果為正數,則該Cookie在maxAge秒之後失效。如果為負數,該Cookie為臨時Cookie,關閉浏覽器即失效,浏覽器也不會以任何形式儲存該Cookie。如果為0,表示删除該Cookie。預設為–1 |
boolean secure | 該Cookie是否僅被使用安全協定傳輸。安全協定。安全協定有HTTPS,SSL等,在網絡上傳輸資料之前先将資料加密。預設為false |
String path | 該Cookie的使用路徑。如果設定為“/sessionWeb/”,則隻有contextPath為“/sessionWeb”的程式可以通路該Cookie。如果設定為“/”,則本域名下contextPath都可以通路該Cookie。注意最後一個字元必須為“/” |
String domain | 可以通路該Cookie的域名。如果設定為“.google.com”,則所有以“google.com”結尾的域名都可以通路該Cookie。注意第一個字元必須為“.” |
String comment | 該Cookie的用處說明。浏覽器顯示Cookie資訊的時候顯示該說明 |
int version | 該Cookie使用的版本号。0表示遵循Netscape的Cookie規範,1表示遵循W3C的RFC 2109規範 |
1.1.7 JavaScript操作Cookie
Cookie是儲存在浏覽器端的,是以浏覽器具有操作Cookie的先決條件。浏覽器可以使用腳本程式如JavaScript或者VBScript等操作Cookie。這裡以JavaScript為例介紹常用的Cookie操作。例如下面的代碼會輸出本頁面所有的Cookie。
<script>document.write(document.cookie);</script>
由于JavaScript能夠任意地讀寫Cookie,有些好事者便想使用JavaScript程式去窺探使用者在其他網站的Cookie。不過這是徒勞的,W3C組織早就意識到JavaScript對Cookie的讀寫所帶來的安全隐患并加以防備了,W3C标準的浏覽器會阻止JavaScript讀寫任何不屬于自己網站的Cookie。換句話說,A網站的JavaScript程式讀寫B網站的Cookie不會有任何結果。
1.1.8 案例:永久登入
如果使用者是在自己家的電腦上上網,登入時就可以記住他的登入資訊,下次通路時不需要再次登入,直接通路即可。實作方法是把登入資訊如賬号、密碼等儲存在Cookie中,并控制Cookie的有效期,下次通路時再驗證Cookie中的登入資訊即可。
儲存登入資訊有多種方案。最直接的是把使用者名與密碼都保持到Cookie中,下次通路時檢查Cookie中的使用者名與密碼,與資料庫比較。這是一種比較危險的選擇,一般不把密碼等重要資訊儲存到Cookie中。
還有一種方案是把密碼加密後儲存到Cookie中,下次通路時解密并與資料庫比較。這種方案略微安全一些。如果不希望儲存密碼,還可以把登入的時間戳儲存到Cookie與資料庫中,到時隻驗證使用者名與登入時間戳就可以了。
另一種方案實作方式是把賬号按照一定的規則加密後,連同賬号一塊儲存到Cookie中。下次通路時隻需要判斷賬号的加密規則是否正确即可。本例把賬号儲存到名為account的Cookie中,把賬号連同密鑰用MD1算法加密後儲存到名為ssid的Cookie中。驗證時驗證Cookie中的賬号與密鑰加密後是否與Cookie中的ssid相等。
提示:該加密機制中最重要的部分為算法與密鑰。由于MD1算法的不可逆性,即使使用者知道了賬号與加密後的字元串,也不可能解密得到密鑰。是以,隻要保管好密鑰與算法,該機制就是安全的。
1.2 Session機制
除了使用Cookie,Web應用程式中還經常使用Session來記錄用戶端狀态。Session是伺服器端使用的一種記錄用戶端狀态的機制,使用上比Cookie簡單一些,相應的也增加了伺服器的存儲壓力。
1.2.1 什麼是Session
Session是另一種記錄客戶狀态的機制,不同的是Cookie儲存在用戶端浏覽器中,而Session儲存在伺服器上。用戶端浏覽器通路伺服器的時候,伺服器把用戶端資訊以某種形式記錄在伺服器上。這就是Session。用戶端浏覽器再次通路時隻需要從該Session中查找該客戶的狀态就可以了。
如果說Cookie機制是通過檢查客戶身上的“通行證”來确定客戶身份的話,那麼Session機制就是通過檢查伺服器上的“客戶明細表”來确認客戶身份。Session相當于程式在伺服器上建立的一份客戶檔案,客戶來訪的時候隻需要查詢客戶檔案表就可以了。
1.2.2 實作使用者登入
Session對應的類為javax.servlet.http.HttpSession類。每個來訪者對應一個Session對象,所有該客戶的狀态資訊都儲存在這個Session對象裡。Session對象是在用戶端第一次請求伺服器的時候建立的。Session也是一種key-value的屬性對,通過getAttribute(Stringkey)和setAttribute(String key,Objectvalue)方法讀寫客戶狀态資訊。Servlet裡通過request.getSession()方法擷取該客戶的Session,
當多個用戶端執行程式時,伺服器會儲存多個用戶端的Session。擷取Session的時候也不需要聲明擷取誰的Session。Session機制決定了目前客戶隻會擷取到自己的Session,而不會擷取到别人的Session。各客戶的Session也彼此獨立,互不可見。
提示:Session的使用比Cookie友善,但是過多的Session存儲在伺服器記憶體中,會對伺服器造成壓力。
1.2.3 Session的生命周期
Session儲存在伺服器端。為了獲得更高的存取速度,伺服器一般把Session放在記憶體裡。每個使用者都會有一個獨立的Session。如果Session内容過于複雜,當大量客戶通路伺服器時可能會導緻記憶體溢出。是以,Session裡的資訊應該盡量精簡。
Session在使用者第一次通路伺服器的時候自動建立。需要注意隻有通路JSP、Servlet等程式時才會建立Session,隻通路HTML、IMAGE等靜态資源并不會建立Session。如果尚未生成Session,也可以使用request.getSession(true)強制生成Session。
Session生成後,隻要使用者繼續通路,伺服器就會更新Session的最後通路時間,并維護該Session。使用者每通路伺服器一次,無論是否讀寫Session,伺服器都認為該使用者的Session“活躍(active)”了一次。
1.2.4 Session的有效期
由于會有越來越多的使用者通路伺服器,是以Session也會越來越多。為防止記憶體溢出,伺服器會把長時間内沒有活躍的Session從記憶體删除。這個時間就是Session的逾時時間。如果超過了逾時時間沒通路過伺服器,Session就自動失效了。
Session的逾時時間為maxInactiveInterval屬性,可以通過對應的getMaxInactiveInterval()擷取,通過setMaxInactiveInterval(longinterval)修改。
Session的逾時時間也可以在web.xml中修改。另外,通過調用Session的invalidate()方法可以使Session失效。
1.2.5 Session的常用方法
Session中包括各種方法,使用起來要比Cookie友善得多。Session的常用方法如表1.2所示。
表1.2 HttpSession的常用方法
方 法 名 | 描 述 |
void setAttribute(String attribute, Object value) | 設定Session屬性。value參數可以為任何Java Object。通常為Java Bean。value資訊不宜過大 |
String getAttribute(String attribute) | 傳回Session屬性 |
Enumeration getAttributeNames() | 傳回Session中存在的屬性名 |
void removeAttribute(String attribute) | 移除Session屬性 |
String getId() | 傳回Session的ID。該ID由伺服器自動建立,不會重複 |
long getCreationTime() | 傳回Session的建立日期。傳回類型為long,常被轉化為Date類型,例如:Date createTime = new Date(session.get CreationTime()) |
long getLastAccessedTime() | 傳回Session的最後活躍時間。傳回類型為long |
int getMaxInactiveInterval() | 傳回Session的逾時時間。機關為秒。超過該時間沒有通路,伺服器認為該Session失效 |
void setMaxInactiveInterval(int second) | 設定Session的逾時時間。機關為秒 |
void putValue(String attribute, Object value) | 不推薦的方法。已經被setAttribute(String attribute, Object Value)替代 |
Object getValue(String attribute) | 不被推薦的方法。已經被getAttribute(String attr)替代 |
boolean isNew() | 傳回該Session是否是新建立的 |
void invalidate() | 使該Session失效 |
Tomcat中Session的預設逾時時間為20分鐘。通過setMaxInactiveInterval(int seconds)修改逾時時間。可以修改web.xml改變Session的預設逾時時間。例如修改為60分鐘:
<session-config>
<session-timeout>60</session-timeout> <!-- 機關:分鐘 -->
</session-config>
注意:<session-timeout>參數的機關為分鐘,而setMaxInactiveInterval(int s)機關為秒。
1.2.6 Session對浏覽器的要求
雖然Session儲存在伺服器,對用戶端是透明的,它的正常運作仍然需要用戶端浏覽器的支援。這是因為Session需要使用Cookie作為識别标志。HTTP協定是無狀态的,Session不能依據HTTP連接配接來判斷是否為同一客戶,是以伺服器向用戶端浏覽器發送一個名為JSESSIONID的Cookie,它的值為該Session的id(也就是HttpSession.getId()的傳回值)。Session依據該Cookie來識别是否為同一使用者。
該Cookie為伺服器自動生成的,它的maxAge屬性一般為–1,表示僅目前浏覽器内有效,并且各浏覽器視窗間不共享,關閉浏覽器就會失效。
是以同一機器的兩個浏覽器視窗通路伺服器時,會生成兩個不同的Session。但是由浏覽器視窗内的連結、腳本等打開的新視窗(也就是說不是輕按兩下桌面浏覽器圖示等打開的視窗)除外。這類子視窗會共享父視窗的Cookie,是以會共享一個Session。
注意:新開的浏覽器視窗會生成新的Session,但子視窗除外。子視窗會共用父視窗的Session。例如,在連結上右擊,在彈出的快捷菜單中選擇“在新視窗中打開”時,子視窗便可以通路父視窗的Session。
請求儲存session id的幾種方式
A.儲存session id的方式可以采用cookie,這樣在互動過程中浏覽器可以自動的按照規則把這個辨別發送給伺服器。
B.由于cookie可以被人為的禁止,必須有其它的機制以便在cookie被禁止時仍然能夠把session id傳遞回伺服器,經常采用的一種技術叫做URL重寫,就是把session id附加在URL路徑的後面,附加的方式也有兩種,一種是作為URL路徑的附加資訊,另一種是作為查詢字元串附加在URL後面。網絡在整個互動過程中始終保持狀态,就必須在每個用戶端可能請求的路徑後面都包含這個session id。如果用戶端Cookie禁用,則伺服器可以自動通過重寫URL的方式來儲存Session的值,并且這個過程對程式員透明。(IE6.0除外)
C.另一種技術叫做表單隐藏字段。就是伺服器會自動修改表單,添加一個隐藏字段,以便在表單送出時能夠把session id傳遞回伺服器。
URL重寫有什麼缺點
對所有的URL使用URL重寫,包括超連結,form的action,和重定向的URL。每個引用你的站點的URL,以及那些傳回給使用者的URL(即使通過間接手段,比如伺服器重定向中的Location字段)都要添加額外的資訊。
這意味着在你的站點上不能有任何靜态的HTML頁面(至少靜态頁面中不能有任何連結到站點動态頁面的連結)。是以,每個頁面都必須使用servlet或JSP動态生成。即使所有的頁面都動态生成,如果使用者離開了會話并通過書簽或連結再次回來,會話的資訊都會丢失,因為存儲下來的連結含有錯誤的辨別資訊-該URL後面的SESSION ID已經過期了。
使用隐藏的表單域有什麼缺點
僅當每個頁面都是有表單送出而動态生成時,才能使用這種方法。單擊正常的<A HREF..>超文本連結并不産生表單送出,是以隐藏的表單域不能支援通常的會話跟蹤,隻能用于一系列特定的操作中,比如線上商店的結賬過程。
來源: <Cookie/Session機制詳解 - 瞧字不識 - 部落格頻道 - CSDN.NET>
http://www.blogjava.net/cheneyfree/archive/2007/05/26/120168.html