- 輕量級Java EE企業(yè)應(yīng)用開發(fā)實(shí)戰(zhàn)
- 柳偉衛(wèi)編著
- 2699字
- 2022-07-29 14:31:21
3.8 會話
HTTP的設(shè)計(jì)天然是無狀態(tài)協(xié)議。為了構(gòu)建有效的Web應(yīng)用程序,必須將來自特定客戶端的請求彼此關(guān)聯(lián),因此衍生出了很多會話(Session)跟蹤機(jī)制。
然而,直接使用這些會話跟蹤機(jī)制是困難或麻煩的。因此,Servlet規(guī)范定義了一個(gè)簡單的HttpSession接口,該接口允許Servlet容器使用多種方法來跟蹤用戶的會話,而無須讓應(yīng)用開發(fā)者感覺到使用這些方法的差別。
3.8.1 會話跟蹤機(jī)制
會話跟蹤機(jī)制有以下幾類:
1.Cookie
通過HTTP Cookie進(jìn)行會話跟蹤是常用的會話跟蹤機(jī)制,要求被所有Servlet容器支持。
容器向客戶端發(fā)送一個(gè)Cookie。然后,客戶端會在后續(xù)每個(gè)請求上攜帶該Cookie,并返回給服務(wù)器,這樣就明確地每個(gè)請求與會話關(guān)聯(lián)關(guān)系。會話是通過Cookie的標(biāo)準(zhǔn)名稱來跟蹤的。Cookie的標(biāo)準(zhǔn)名稱必須是JSESSIONID.Containers格式,允許通過容器特定配置來定制Cookie的名稱。
所有Servlet容器必須提供配置容器是否將會話跟蹤C(jī)ookie標(biāo)記為HttpOnly的能力。已建立的配置必須適用于尚未建立上下文特定配置的所有上下文。
如果Web應(yīng)用程序?yàn)槠鋾捀機(jī)ookie配置自定義名稱,而會話ID在URL中進(jìn)行了編碼(前提是已啟用URL重寫),那么同樣的自定義名稱也將用作URI參數(shù)的名稱。
2.SSL會話
安全套接字層是HTTPS協(xié)議中使用的加密技術(shù),它有一個(gè)內(nèi)置的機(jī)制,可以允許客戶端的多個(gè)請求被明確地標(biāo)識為會話的一部分。Servlet容器可以很容易地使用這些數(shù)據(jù)來定義會話。
3.URL重寫
URL重寫是會話跟蹤的最小公分母。當(dāng)客戶端不接受Cookie時(shí),URL重寫可能被服務(wù)器用作會話跟蹤的基礎(chǔ)。URL重寫涉及將數(shù)據(jù)(一個(gè)會話ID)添加到由容器解釋的URL路徑,以將請求與會話關(guān)聯(lián)起來。
會話ID必須編碼為URL字符串中的路徑參數(shù)。參數(shù)的名稱必須是jsessionid。下面是一個(gè)包含編碼路徑信息的URL示例:
http://waylau.com/catalog/index.html;jsessionid=1234
URL重寫在日志、書簽、引用標(biāo)頭、緩存的HTML和URL欄中公開會話標(biāo)識符。URL重寫不應(yīng)該被用作一個(gè)會話跟蹤機(jī)制,在這個(gè)機(jī)制中,Cookie或SSL會話是受支持的和合適的。
4.會話的完整性
Web容器必須能夠支持HTTP會話,同時(shí)為不支持使用Cookie的客戶端提供HTTP請求。為了滿足這個(gè)需求,Web容器通常支持URL重寫機(jī)制。
3.8.2 創(chuàng)建會話
當(dāng)會話只是一個(gè)預(yù)期的會話且尚未建立時(shí),它會被認(rèn)為是“新”的。因?yàn)镠TTP是基于請求-響應(yīng)的協(xié)議,所以在客戶端“加入”之前HTTP會話被認(rèn)為是新的。當(dāng)會話跟蹤信息被返回到服務(wù)器,表明已經(jīng)建立了會話時(shí),客戶端加入會話。在客戶端加入會話之前,不能假定客戶端的下一個(gè)請求將被視為會話的一部分。
如果下列任何一項(xiàng)是正確的,會話就會被認(rèn)為是新的:
- 客戶端還不了解會話。
- 客戶端選擇不加入一個(gè)會話。
如果會話被認(rèn)為是“新”的,則證明這個(gè)“新”會話是沒有跟之前的請求有任何關(guān)聯(lián)關(guān)系的。
Servlet開發(fā)人員必須設(shè)計(jì)應(yīng)用程序來處理客戶端沒有、不能或不會加入會話的情況。
與每個(gè)會話相關(guān)聯(lián),有一個(gè)包含一個(gè)獨(dú)特標(biāo)識符的字符串,被稱為會話ID。會話ID的值可以通過調(diào)用javax.servlet.http.HttpSession.getId()獲取,創(chuàng)建后可以通過調(diào)用javax.servlet.http.HttpServletRequest.changeSessionId()改變。
3.8.3 會話范圍
HttpSession對象必須在應(yīng)用程序(或Servlet上下文)級別范圍內(nèi)。在底層機(jī)制,例如用于建立會話的Cookie,對于不同的上下文可以是相同的,但是引用的對象(包括該對象中的屬性)不能被容器在上下文之間共享。
比如,如果一個(gè)Servlet使用RequestDispatcher在另一個(gè)Web應(yīng)用程序中調(diào)用Servlet,那么為這個(gè)Servlet創(chuàng)建并可見的任何會話都必須與調(diào)用Servlet可見的會話不同。
此外,上下文的會話必須可被請求恢復(fù)到該上下文,無論它們的關(guān)聯(lián)上下文是否被直接訪問或在創(chuàng)建會話時(shí)作為請求分派的目標(biāo)。
3.8.4 綁定屬性到會話
Servlet可以通過名稱將對象屬性綁定到HttpSession實(shí)現(xiàn)。任何綁定到會話的對象都可用于屬于同一個(gè)ServletContext的任何其他Servlet,并處理被標(biāo)識為同一會話的一部分請求。
當(dāng)某些對象被放置或從會話中刪除時(shí),可能需要通知。此信息可以通過對象實(shí)現(xiàn)HttpSessionBindingListener接口獲得。該接口定義了以下方法,這些方法將指示被綁定到的對象或從會話中釋放的對象。
- valueBound
- valueUnbound
在通過HttpSession接口的getAttribute方法提供對象之前,必須調(diào)用valueBound方法。在對象不再通過HttpSession接口的getAttribute方法可達(dá)之后,必須調(diào)用valueUnbound方法。
3.8.5 會話超時(shí)
在HTTP協(xié)議中,當(dāng)客戶端不再活動時(shí),是沒有顯式地終止信號的。這意味著唯一可以用來指示客戶端不再活動的機(jī)制是超時(shí)時(shí)間。
會話的默認(rèn)超時(shí)時(shí)間由Servlet容器定義,可以通過ServletContext接口的getSessionTimeout方法或HttpSession接口的getMaxInactiveInterval方法獲得。
這個(gè)時(shí)間可以由開發(fā)人員使用ServletContext接口的setSessionTimeout方法或HttpSession接口的setMaxInactiveInterval方法來更改。會話超時(shí)方法使用的超時(shí)時(shí)間定義為分鐘。setMaxInactiveInterval方法所使用的超時(shí)時(shí)間定義為秒。根據(jù)定義,如果會話的超時(shí)時(shí)間設(shè)置為0或更低的值,會話就不會過期。在使用該會話的所有Servlet退出服務(wù)方法之前,會話將不會失效。一旦會話失效開始,新的請求就不能看到會話。
3.8.6 最后訪問時(shí)間
HttpSession接口的getLastAccessedTime方法允許Servlet在當(dāng)前請求之前確定會話上次訪問的時(shí)間。當(dāng)會話的一部分請求首先由Servlet容器處理時(shí),會話被認(rèn)為是被訪問的。
3.8.7 線程問題
執(zhí)行請求線程的多個(gè)Servlet可以同時(shí)對同一個(gè)會話對象進(jìn)行活動訪問。容器必須確保對表示會話屬性的內(nèi)部數(shù)據(jù)結(jié)構(gòu)的操作以線程安全的方式執(zhí)行。開發(fā)人員負(fù)責(zé)線程安全訪問屬性對象本身。這將保護(hù)HttpSession對象內(nèi)的屬性集合不受并發(fā)訪問,從而消除了應(yīng)用程序?qū)е略摷媳黄茐牡臋C(jī)會。除非在規(guī)范中有明確的聲明,否則從請求或響應(yīng)中獲得的對象必須被假定為非線程安全的。這包括但不限于ServletResponse.getWriter()方法返回的PrintWriter和ServletResponse.getOutputStream()方法返回的OutputStream。
3.8.8 分布式環(huán)境
在分布式的應(yīng)用程序中,所有屬于會話的請求必須一次由一個(gè)JVM處理。容器必須能夠正確地使用setAttribute或putValue方法對所有放置到HttpSession類實(shí)例中的對象進(jìn)行處理。為了滿足這些條件,我們實(shí)施了以下限制:
- 容器必須接受實(shí)現(xiàn)Serializable接口的對象。
- 容器可以選擇支持HttpSession中其他指定對象的存儲,例如對Enterprise JavaBeans組件和事務(wù)的引用。
- 遷移會話將由特定容器設(shè)施處理。
當(dāng)分布式Servlet容器不支持必需的會話遷移存儲對象機(jī)制時(shí),容器必須拋出IllegalArgumentException。
分布式Servlet容器必須支持遷移的對象實(shí)現(xiàn)Serializable的必要機(jī)制。
這些限制意味著開發(fā)人員可以確保在非分布式容器中不存在其他并發(fā)性問題。
容器提供程序可以通過將會話對象及其內(nèi)容(從分布式系統(tǒng)的任何活動節(jié)點(diǎn))移動到系統(tǒng)的不同節(jié)點(diǎn),從而確保負(fù)載均衡和故障轉(zhuǎn)移等服務(wù)特性的可伸縮性和質(zhì)量。
如果分布式容器持久化或遷移會話以提供服務(wù)特性的質(zhì)量,那么它們并不局限于使用本機(jī)JVM序列化機(jī)制來序列化httpsession及其屬性。開發(fā)人員不能保證容器會在會話屬性上調(diào)用readObject和writeObject方法,如果實(shí)現(xiàn)了這些方法,那么保證它們的屬性的可序列化閉包將被保留。
容器必須在遷移會話期間通知任何實(shí)現(xiàn)HttpSessionActivationListener的會話屬性。它們必須在會話序列化之前通知偵聽器,并在會話反序列化之后激活。編寫分布式應(yīng)用程序的開發(fā)人員應(yīng)該意識到,由于容器可能在多個(gè)Java虛擬機(jī)中運(yùn)行,因此開發(fā)人員不能依賴靜態(tài)變量來存儲應(yīng)用程序狀態(tài),應(yīng)該使用企業(yè)Bean或數(shù)據(jù)庫存儲這些狀態(tài)。
3.8.9 客戶端語義
由于Cookie或SSL證書通常由Web瀏覽器進(jìn)程控制,且不與瀏覽器的任何特定窗口相關(guān)聯(lián),因此從客戶端應(yīng)用程序的所有窗口向Servlet容器請求的可能是同一會話的一部分。為了獲得最大的可移植性,開發(fā)人員應(yīng)該始終假設(shè)客戶的所有窗口都參與了相同的會話。
- iOS面試一戰(zhàn)到底
- Python編程自學(xué)手冊
- Objective-C Memory Management Essentials
- Game Programming Using Qt Beginner's Guide
- Learning SAP Analytics Cloud
- 跟老齊學(xué)Python:輕松入門
- Python:Master the Art of Design Patterns
- Mastering Apache Maven 3
- Highcharts Cookbook
- 編程與類型系統(tǒng)
- 計(jì)算機(jī)應(yīng)用基礎(chǔ)案例教程
- Hands-On Nuxt.js Web Development
- Python Data Science Cookbook
- HoloLens與混合現(xiàn)實(shí)開發(fā)
- 寫給程序員的Python教程