javax.servlet.http.HttpSession session用法詳解
HttpSession類它提供了setAttribute()和getAttribute()方法存儲和檢索對象。
HttpSession提供了一個會話ID關鍵字,一個參與會話行為的客戶端在同一會話的請求中存儲
和返回它。servlet引擎查找適當的會話對象,並使之對當前請求可用。HttpServletRequest
接口提供了以下方法來獲取HttpSession實例。
public HttpSession getSession() :該方法取得請求所在的會話。
public HttpSession getSession(Boolean create):返回當前請求的會話。如果當前請求不
屬於任何會話,而且create參數為true,則創建一個會話,否則返回null。此後所有來自同
一個的請求都屬於這個會話,通過它的getSession返回的是當前會話。
方法
介紹
public void setAttribute(String name,Object value)
將value對象以name名稱綁定到會話
public object getAttribute(String name)
取得name的屬性值,如果屬性不存在則返回null
public void removeAttribute(String name)
從會話中刪除name屬性,如果不存在不會執行,也不會拋處錯誤.
public Enumeration getAttributeNames()
返回和會話有關的枚舉值
public void invalidate()
使會話失效,同時刪除屬性對象
public Boolean isNew()
用於檢測當前客戶是否為新的會話
public long getCreationTime()
返回會話創建時間
public long getLastAccessedTime()
返回在會話時間內web容器接收到客戶最後發出的請求的時間
public int getMaxInactiveInterval()
返回在會話期間內客戶請求的最長時間.秒
public void setMasInactiveInterval(int seconds)
允許客戶客戶請求的最長時間
ServletContext getServletContext()
返回當前會話的上下文環境,ServletContext對象可以使Servlet與web容器進行通信
public String getId()
返回會話期間的識別號
HttpSession是Java平台對session機制的實現規范,因為它僅僅是個接口,具體到每個
web應用服務器的提供商,除了對規范支持之外,仍然會有一些規范裡沒有規定的細微差異。
這裡我們以BEA的Weblogic Server8.1作為例子來演示。
首先,Weblogic Server提供了一系列的參數來控制它的HttpSession的實現,包括使用
cookie的開關選項,使用URL重寫的開關選項,session持 久化的設置,session失效時間的
設置,以及針對cookie的各種設置,比如設置cookie的名字、路徑、域,cookie的生存時間
等。
一般情況下,session都是存儲在內存裡,當服務器進程被停止或者重啟的時候,內存裡
的session也會被清空,如果設置了 session的持久化特性,服務器就會把session保存到硬
盤上,當服務器進程重新啟動或這些信息將能夠被再次使用,Weblogic Server支持的持久性
方式包括文件、數據庫教程、客戶端cookie保存和復制。
復制嚴格說來不算持久化保存,因為session實際上還是保存在內存裡,不過同樣的信息
被復制到各個cluster內的服務器進程中,這樣即使某個服務器進程停止工作也仍然可以從其
他進程中取得session。
cookie生存時間的設置則會影響浏覽器生成的cookie是否是一個會話cookie。默認是使
用會話cookie。有興趣的可以用它來試驗我們在第四節裡提到的那個誤解。
cookie的路徑對於web應用程序來說是一個非常重要的選項,Weblogic Server對這個選
項的默認處理方式使得它與其他服務器有明顯的區別。後面我們會專題討論。
關於session的設置參考[5] http://e-
docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869
六、HttpSession常見問題
(在本小節中session的含義為⑤和⑥的混合)
1、session在何時被創建
一個常見的誤解是以為session在有客戶端訪問時就被創建,然而事實是直到某server端
程序調用 HttpServletRequest.getSession(true)這樣的語句時才被創建,注意如果JSP沒有
顯示的使用 <%@page session="false"%> 關閉session,則JSP文件在編譯成Servlet時將會
自動加上這樣一條語句HttpSession session = HttpServletRequest.getSession(true);這
也是JSP中隱含的session對象的來歷。
由於session會消耗內存資源,因此,如果不打算使用session,應該在所有的JSP中關閉
它。
2、session何時被刪除
綜合前面的討論,session在下列情況下被刪除a.程序調用HttpSession.invalidate();
或b.距離上一次收到客戶端發送的session id時間間隔超過了session的超時設置;或c.服務
器進程被停止(非持久session)
3、如何做到在浏覽器關閉時刪除session
嚴格的講,做不到這一點。可以做一點努力的辦法是在所有的客戶端頁面裡使用
網頁特效代碼window.onclose來監視浏覽器的關閉 動作,然後向服務器發送一個請求來刪
除session。但是對於浏覽器崩潰或者強行殺死進程這些非常規手段仍然無能為力。
4、有個HttpSessionListener是怎麼回事
你可以創建這樣的listener去監控session的創建和銷毀事件,使得在發生這樣的事件時
你可以做一些相應的工作。注意是 session的創建和銷毀動作觸發listener,而不是相反。
類似的與HttpSession有關的listener還有 HttpSessionBindingListener,
HttpSessionActivationListener和 HttpSessionAttributeListener。
5、存放在session中的對象必須是可序列化的嗎
不是必需的。要求對象可序列化只是為了session能夠在集群中被復制或者能夠持久保存
或者在必要時server能夠暫時把session交換出內 存。在Weblogic Server的session中放置
一個不可序列化的對象在控制台上會收到一個警告。我所用過的某個iPlanet版本如果
session中有不可序列化 的對象,在session銷毀時會有一個Exception,很奇怪。
6、如何才能正確的應付客戶端禁止cookie的可能性
對所有的URL使用URL重寫,包括超鏈接,form的action,和重定向的URL,具體做法參見
[6]
http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770
7、開兩個浏覽器窗口訪問應用程序會使用同一個session還是不同的session
參見第三小節對cookie的討論,對session來說是只認id不認人,因此不同的浏覽器,不
同的窗口打開方式以及不同的cookie存儲方式都會對這個問題的答案有影響。
8、如何防止用戶打開兩個浏覽器窗口操作導致的session混亂
這個問題與防止表單多次提交是類似的,可以通過設置客戶端的令牌來解決。就是在服
務器每次生成一個不同的id返回給客戶端,同時保存在 session裡,客戶端提交表單時必須
把這個id也返回服務器,程序首先比較返回的id與保存在session裡的值是否一致,如果不一
致則說明本次操 作已經被提交過了。可以參看《J2EE核心模式》關於表示層模式的部分。需
要注意的是對於使用javascript window.open打開的窗口,一般不設置這個id,或者使用單
獨的id,以防主窗口無法操作,建議不要再window.open打開的窗口裡做修改 操作,這樣就
可以不用設置。
9、為什麼在Weblogic Server中改變session的值後要重新調用一次session.setValue
做這個動作主要是為了在集群環境中提示Weblogic Server session中的值發生了改變,需要
向其他服務器進程復制新的session值。
10、為什麼session不見了
排除session正常失效的因素之外,服務器本身的可能性應該是微乎其微的,雖然筆者在
iPlanet6SP1加若干補丁的Solaris版本上倒 也遇到過;浏覽器插件的可能性次之,筆者也遇
到過3721插件造成的問題;理論上防火牆或者代理服務器在cookie處理上也有可能會出現問
題。
出現這一問題的大部分原因都是程序的錯誤,最常見的就是在一個應用程序中去訪問另
外一個應用程序。我們在下一節討論這個問題。
七、跨應用程序的session共享
常常有這樣的情況,一個大項目被分割成若干小項目開發,為了能夠互不干擾,要求每
個小項目作為一個單獨的web應用程序開發,可是到了最後突然發現某 幾個小項目之間需要
共享一些信息,或者想使用session來實現SSO (single sign on),在session中保存login的
用戶信息,最自然的要求是應用程序間能夠訪問彼此的session。
然而按照Servlet規范,session的作用范圍應該僅僅限於當前應用程序下,不同的應用
程序之間是不能夠互相訪問對方的session 的。各個應用服務器從實際效果上都遵守了這一
規范,但是實現的細節卻可能各有不同,因此解決跨應用程序session共享的方法也各不相同
。
首先來看一下Tomcat是如何實現web應用程序之間session的隔離的,從Tomcat設置的
cookie路徑來看,它對不同的應用程序設置 的cookie路徑是不同的,這樣不同的應用程序所
用的session id是不同的,因此即使在同一個浏覽器窗口裡訪問不同的應用程序,發送給服
務器的session id也可以是不同的。
Session詳解 Session詳解
根據這個特性,我們可以推測Tomcat中session的內存結構大致如下。
Session詳解
筆者以前用過的iPlanet也采用的是同樣的方式,估計SunONE與iPlanet之間不會有太大
的差別。對於這種方式的服務器,解決的思路很簡 單,實際實行起來也不難。要麼讓所有的
應用程序共享一個session id,要麼讓應用程序能夠獲得其他應用程序的session id。
iPlanet中有一種很簡單的方法來實現共享一個session id,那就是把各個應用程序的
cookie路徑都設為/(實際上應該是/NASApp,對於應用程序來講它的作用相當於根)。
<session-info>
<path>/NASApp</path>
</session-info>
需要注意的是,操作共享的session應該遵循一些編程約定,比如在session attribute
名字的前面加上應用程序的前綴,使得setAttribute("name", "neo")變成setAttribute
("app1.name", "neo"),以防止命名空間沖突,導致互相覆蓋。
在Tomcat中則沒有這麼方便的選擇。在Tomcat版本3上,我們還可以有一些手段來共享
session。對於版本4以上的 Tomcat,目前筆者尚未發現簡單的辦法。只能借助於第三方的力
量,比如使用文件、數據庫、JMS或者客戶端cookie,URL參數或者隱藏字段等手 段。
我們再看一下Weblogic Server是如何處理session的。
Session詳解
Session詳解
從截屏畫面上可以看到Weblogic Server對所有的應用程序設置的cookie的路徑都是/,
這是不是意味著在Weblogic Server中默認的就可以共享session了呢?然而一個小實驗即可
證明即使不同的應用程序使用的是同一個session,各個應用程序仍然只能訪問 自己所設置
的那些屬性。這說明Weblogic Server中的session的內存結構可能如下
Session詳解
對於這樣一種結構,在session機制本身上來解決session共享的問題應該是不可能的了
。除了借助於第三方的力量,比如使用文件、數據庫、 JMS或者客戶端cookie,URL參數或者
隱藏字段等手段,還有一種較為方便的做法,就是把一個應用程序的session放到
ServletContext中,這樣另外一個應用程序就可以從ServletContext中取得前一個應用程序
的引用。示例代碼如下,
應用程序A
context.setAttribute("appA", session);
應用程序B
contextA = context.getContext("/appA");
HttpSession sessionA = (HttpSession)contextA.getAttribute("appA");
值得注意的是這種用法不可移植,因為根據ServletContext的JavaDoc,應用服務器可以
處於安全的原因對於context.getContext("/appA");返回空值,以上做法在Weblogic Server
8.1中通過。
那麼Weblogic Server為什麼要把所有的應用程序的cookie路徑都設為/呢?原來是為了
SSO,凡是共享這個session的應用程序都可以共享認證的信息。一 個簡單的實驗就可以證明
這一點,修改首先登錄的那個應用程序的描述符weblogic.xml,把cookie路徑修改為/appA
訪問另外一個應用程序會重新要求登錄,即使是反過來,先訪問cookie路徑為/的應用程序,
再訪問修改過路徑的這個,雖然不再提示登錄,但是登錄的用戶 信息也會丟失。注意做這個
實驗時認證方式應該使用FORM,因為浏覽器和web服務器對basic認證方式有其他的處理方式
,第二次請求的認證不是通過 session來實現的。具體請參看[7] secion 14.8
Authorization,你可以修改所附的示例程序來做這些試驗。