對於在客戶機而不是服務器上存儲會話數據,有兩種主要的解決方案 - cookie 和隱藏域(hidden fIEld)- 每一種方案都有各自的優勢和劣勢。 隱藏域 出現的第一種保留會話數據的機制之一是使用“隱藏域”。這種選擇依賴於 HTML 的一個特殊功能來保存會話信息。HTML INPUT 標記有幾種不同的類型,可以讓 Web 作者指定如何接收輸入。例如,“TEXT”類型會導致浏覽器顯示一個文本域。然而,有一種輸入類型不符合任何特定的 UI 部件:“HIDDEN” 類型。隱藏域沒有 UI 表現形式,所以不能被浏覽器用戶改變。隱藏域的值可以在 Web 頁面上由應用服務器設置,以後再通過 HTTPServletRequest 接口的 getParameter() 方法讀取回來,就好像其它所有 HTML 域一樣。這正是我們在客戶機 HTML 中記錄會話信息所需要的。使用隱藏域存儲會話數據的一個主要缺點是,我們必須改寫 HTML 使其包括這些新的信息。下面的 Java 代碼指出要做下面的工作: out.print("input type=\"HIDDEN\"); out.print("NAME=\"FieldName\" value=\""); out.print(fIEldValue); out.println("\"\>"); 隱藏域使用很簡單,正因如此,它們也存在一些問題以至於對很多系統來說不適用。第一個問題是,JSDK 沒有提供一種將任意對象移出和移入隱藏域的方法。在 JSDK 中,處理隱藏域的方法和處理其它 HTTP 參數的方法是一樣的,也就是說隱藏域是作為字符串來處理的。如果您想在自己的程序中使用這些字符串裡面的信息,就必須建立一個架構用來生成合適的 HTML 和重新解析信息。 另一個缺點是,您的會話信息最終會在網絡上發送很多次。為了理解這是怎樣發生的,考慮下面的情況: 我們的酒類購物站點有一個兩頁的“頻繁顧客程序”。第一頁接收用戶信息(姓名、地址等),而第二頁接收選擇信息(您更喜歡加州酒還是法國酒?您喜歡夏敦埃酒還是墨爾樂紅葡萄酒?諸如此類等等)。第一個 servlet 從前一個 HTML 表單中解析出用戶信息,並將其記錄在隱藏域中。第二個 servlet 不僅必須解析出新的 HTML 表單信息,還要解析出前一個 servlet 重新寫入隱藏域的信息。這樣,每一個後續的頁面不斷地增加要解析的信息,當您進一步繼續處理時就會使下載時間變長。 使用隱藏域的一個最大的缺點存在於混合型的站點中。新的 servlet 實現必須與舊的 CGI 和 HTML 共存很多次。如果您不能修改這些頁面,當用戶在 servlet 和舊的頁面之間遍歷時,隱藏數據就會丟失。 假定我們的酒類購物站點一直是用 CGI 程序實現的。並且假定舊的購物車是作為 www.winesRus.com/cgi-bin/shoppingcart 來實現的。站點中目前所有的 HTML 頁面都指向這個 URL。我們可以改變所有的頁面讓它們指向 www.winesRus.com/servlets/shoppingcart,或者我們可以簡單地用舊的鏈接給 servlet 起一個別名。如果您有數百個目錄頁指向這個鏈接,這個別名可以省掉您很多時間。然而,如果您是用隱藏域遍歷站點,這種解決方法就不起作用。隱藏域必須添加到舊的 HTML 中,而且您可能還要改變這些鏈接。 使用隱藏域還有其它的缺點(請參閱下面章節有關安全性的討論),但是對於某些情況,他們還是不錯的。在安全性不高的某些站點中可以考慮它們,那裡舊頁面浏覽最少,而且頁面不是大量地“建立”在彼此之上,還有在不能接受服務器親緣關系的地方也可以考慮它們。在為隱藏域的生成而建立框架和解析這兩個步驟上付出的一些簡單的努力以後可能會帶來很多好處。然而,正如我們所見到的,對於大多數情況,都會有更好的解決方案。 Cookie 我們要研究的下一個存儲會話數據的方法是直接在 cookie 中存儲數據。正如我們在 HTTPSessions 的討論中已經看到的,cookIE 也可以用於在客戶機浏覽器上存儲信息。它們不僅可以用於存儲客戶機身份標識,還可以存儲實際的會話數據本身。cookie 有巨大的優勢。 首先,cookie 不需要 HTML 重寫。您將會話數據轉換成如隱藏域示例所示的字符串,然後將其添加到您的 HttpServletResponse 對象中,如下所示: package com.winesrus.tests; import javax.servlet.http.*; public class CookieTest { public CookieTest(HttpServletResponse resp, String state) { String warning = Please accept this Cookie or bad things will happen to you!" Cookie cookie = new Cookie("winecookie", state); cookie.setDomain(".winesrus.com"); cookie.setPath("/"); cookie.setComment(warning); cookie.setVersion(0); resp.addCookie(cookie); } } 您可以用以下語句檢索 cookie: javax.servlet.http.HttpServletRequest.getCookies(); 就是這樣。您沒有必要重寫 HTML 頁面來獲取和設置 cookie 數據。 關於 cookIE 的另一個優點就是,您可以和非 Java 資源共享您的會話數據。您的 Javascript 和 CGI 程序可以利用這種狀態信息,因為它是通過每一個客戶機請求發送的。 然而,容量限制可能是使用 cookie 存儲會話數據的致命弱點。一個 cookie 頭最多可以存儲 4K 的文本。這就讓存儲大的數據集合很不實際。您還需要注意您決定在 cookIE 中存儲的內容。這個 cookie 頭包括了您 Web 站點中的所有 cookie。如果超過了最大的 cookie 容量,就會出現不好的後果。例如,依照用戶浏覽器的不同,不是舊 cookie 會丟失就是新 cookie 不能寫入。要避免這種情況,要確保您的 cookie 不接近 4K 的限制。您要給自己足夠的空間,來應付用戶在自己的浏覽器中已經有別的來自您的域的 cookie 的情況。這樣就必須編寫代碼檢查 cookie 是否被成功寫入。您當然不能盲目地將對象序列化後將它們放到 cookie 中去。您必須非常有選擇性的組織例程。 另一個主要的缺點是用戶可以隨意關掉 cookie。現在大多數浏覽器都支持 cookIE。然而,有少數 Web 站點用戶希望在自己的浏覽器中禁用 cookIE。這就強迫您要在 Html 頁面中編寫 JavaScript 或在 servlet 中編碼以檢測用戶浏覽器中的 cookie 是否被打開。所以,如果您使用 cookIE 作為會話數據存儲機制,您就必須有另一種機制作為“退路”,或者通報您的用戶該站點沒有 cookie 無法正常運行。 cookie 還有另一個缺點,就是關於它們怎樣在 Web 中傳送是有限制的。它們不能傳送給同級域名。 這麼說吧,WinesRus 買了一個新的域名:BeerIsUs.com。我們希望用戶能夠使用同樣的購物車付帳(www.winesRus.com/servlets/shoppingcart)。問題在於,使用 cookie 的話,我們站點的 WinesRus.com 部分無法看到 BeerIsUs.com 創建的 cookIE,反過來也一樣。注意,如果我們有一個名為 Commerce.WinesRus.com 的特定的商業服務器,它就能夠看到在 WinesRus.com 創建的 cookie,但是 WinesRus.com 看不到 Commerce.WinesRus.com 創建的 cookie。 在單獨域名上可以存在的 cookIE 數目是有限制的。某些域名限制是與浏覽器有關的,所以您必須用多個浏覽器來作全面的測試以確保 cookie 按照設計運行。使用 cookie 會使域名問題非常復雜。 cookIE 和隱藏域都有另一個主要的缺點:安全性。 在客戶機上存儲的狀態信息是不安全的。除非您花時間將數據加密,您在因特網上來回發送的數據都是明文。即使您手工或使用 SSL 將數據加密,您仍然不會願意在客戶機上存儲敏感的商業數據。 所以,使用客戶機保存會話的不利情況可能會使會話數據存儲機制的優勢黯然失色。客戶機端解決方案最初看起來可能很簡單,但復雜性很快就會成倍增長。相反,我們需要尋找那些最初比較復雜但會帶來更好的整體效果的解決方案。