全部HTTP服務器技術都廣泛採用HTTP會話的概念,而且Java EE也在規範中添加了對會話的支持。html
維持狀態
會話用於維持請求和請求之間的狀態。HTTP請求自身是徹底無狀態的。從服務器的角度來講,當用戶的Web瀏覽器打開第一個鏈接到服務器的套接字時請求就開始了,直到服務器返回最後一個數據包並關閉鏈接時,該請求將結束。
在無狀態方式下,應用程序一般沒法正常工做。一個經典的例子就是在線購物網站。當你瀏覽商店時,找到了喜歡的商品,就會將它添加到購物車中。而後繼續瀏覽商店並找到另外一個喜歡的商品,一樣也將它添加到購物車中。在查看購物車時,你應該看到其中有兩個商品。在你發出的多個請求之間,網站經過某種方式瞭解到它們是來自於同一計算機中的同一瀏覽器,並將它們關聯到你的購物車。其餘人是沒法看到你的購物車或購物車中包含的產品——購物車只綁定到你的計算機和瀏覽器。java
記住用戶
另外一個須要考慮的場景是用戶論壇網站。用戶在登陸以後,就能夠添加論壇主題、回覆主題、參與其餘用戶的私人討論、向版主舉報主題或回覆、還能夠收藏主題。注意在整個過程當中用戶只須要登陸一次。系統須要經過某種方式記住該用戶,會話就提供了這種功能。瀏覽器
在Web會話的理論中,會話是由服務器或Web應用程序管理的某些文件、內存片斷、對象或者容器,它包含了分配給它的各類不一樣數據。
一般會話被賦予一個隨機生成的字符串,稱爲會話ID。第一次建立會話時(即收到請求時),建立的會話ID將會做爲響應的一部分返回到用戶瀏覽器中。接下來從該用戶瀏覽器中發出的請求都將經過某種方式包含該會話ID。當應用程序收到含有會話ID的請求時,它能夠經過該ID將現有會話與當前請求關聯起來。安全
注意:使用隨機字符串做爲會話ID而不使用簡單的序列ID,是由於序列ID是可預測的,這樣可能會容易引發會話劫持。服務器
HTTP 1.1的解決方案HTTP cookie,可用於將會話ID發送到瀏覽器,從而使瀏覽器能夠在將來的請求中包含該會話ID。
cookie有各類不一樣的特性,例如域名、路徑、過時日期或最大生命週期、安全標誌或只含HTTP的標誌。cookie
在Java EE應用服務器中,會話cookie的名字默認爲JSESSIONID。session
另外一種傳輸會話ID的流行方式是經過URL。
不一樣的技術對如何在URL中內嵌和定位會話ID使用不一樣的策略。
Java EE應用程序將會話ID添加到URL的最後一個路徑段的矩陣參數中。經過這種方式分離開會話ID與查詢字符串的參數,使它們不會相互衝突。
http://www.example.com/support?JSESSIONID=NRxclGg2vG7kI4MdlLn?foo=bar&high=five
這種方式使瀏覽器意識不到會話ID的存在。相反,服務器將重寫Location頭中的URL以及任何響應內容中URL,使瀏覽器用於訪問服務器的全部URL都已經內嵌了會話ID。
HttpServletResponse接口定義了兩個能夠重寫URL的方法:encodeURL
和encodeRedirectURL
,它們將在必要的時候把會話ID內嵌在URL中。dom
在許多狀況下,均可以在Java EE中直接使用HTTP會話,不須要添加顯式的配置。不過能夠在部署描述符中配置它們,而且出於安全的目的也應該配置。jsp
<session-config> <session-timeout>30</session-timeout> <cookie-config> <name>JSESSIONID</name> <domain>example.org</domain> <path>/shop</path> <comment><![CDATA[Keeps you logged in. See our privacy policy for more information.]]></comment> <http-only>true</http-only> <secure>false</secure> <max-age>1800</max-age> </cookie-config> <tracking-mode>COOKIE</tracking-mode> <tracking-mode>URL</tracking-mode> <tracking-mode>SSL</tracking-mode> </session-config>
標籤<session-timeout>
以分鐘爲單位,若是它的值小於等於0,那麼會話將永遠也不過時。若是忽略該標籤,那麼它將使用容器的默認值。Tomcat容器的默認值是30分鐘,能夠經過Tomcat配置修改。
Servlet 3.0/Java EE6中新增的標籤<tracking-mode>
用於表示容器應該使用哪一種技術追蹤會話ID。它的合法值有:URL、COOKIE和SSL。
只有在追蹤模式中使用了COOKIE時,纔可使用<cookie-config>
標籤。
<cookie-config>
標籤的標籤:ide
<name>
能夠自定義會話cookie的名字。默認值爲JSESSIONID。<domain>
和<path>
對應着cookie的Domain和Path特性。Web容器已經設置了正確的默認值,所以一般不須要自定義它們。<comment>
將在會話ID cookie中添加Comment特性,用於解釋cookie的目的。<http-only>
和<secure>
對應着cookie的HttpOnly特性和Secure特性,它們的默認值都是假。<max-age>
指定了cookie的Max-Age特性,用於控制cookie什麼時候過時。默認狀況下,cookie沒有過時日期,這意味着它將在瀏覽器關閉時過時,將它設置爲-1的效果相同。private void addToCart(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int productId; try { productId = Integer.parseInt(request.getParameter("productId")); } catch(Exception e) { response.sendRedirect("shop"); return; } HttpSession session = request.getSession(); if(session.getAttribute("cart") == null) session.setAttribute("cart", new Hashtable<Integer, Integer>()); @SuppressWarnings("unchecked") Map<Integer, Integer> cart = (Map<Integer, Integer>)session.getAttribute("cart"); if(!cart.containsKey(productId)) cart.put(productId, 0); cart.put(productId, cart.get(productId) + 1); response.sendRedirect("shop?action=viewCart"); }
HttpServletRequest的getSession方法有兩種方式:getSession()
和getSession(boolean)
。
對getSession()
的調用實際將會調用getSession(true)
,若是會話存在,就返回已有會話,不存在,就建立一個新的會話。
若是調用getSession(false)
,那麼若是會話存在就返回已有會話,不然返回null
。
HttpSession的getAttribute
方法將返回會話中存儲的對象,setAttribute
方法將把對象綁定到會話中。
viewCart.jsp
<%@ page import="java.util.Map" %> <!DOCTYPE html> <html> <head> <title>View Cart</title> </head> <body> <h2>View Cart</h2> <a href="<c:url value="/shop" />">Product List</a><br /><br /> <a href="<c:url value="/shop?action=emptyCart" />">Empty Cart</a><br /><br /> <% @SuppressWarnings("unchecked") Map<Integer, String> products = (Map<Integer, String>)request.getAttribute("products"); @SuppressWarnings("unchecked") Map<Integer, Integer> cart = (Map<Integer, Integer>)session.getAttribute("cart"); if(cart == null || cart.size() == 0) out.println("Your cart is empty."); else { for(int id : cart.keySet()) { out.println(products.get(id) + " (qty: " + cart.get(id) + ")<br />"); } } %> </body> </html>
該JSP使用隱式的session變量訪問會話中存儲的商店購物車Map,而後列出了購物車中全部的產品和它們的數量。
示例源碼(在會話中存儲和獲取數據)連接:https://pan.baidu.com/s/1KYDagePVcW0SXzk9WK3Z1Q 密碼:fz21
private void emptyCart(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getSession().removeAttribute("cart"); response.sendRedirect("shop?action=viewCart"); }
也能夠經過調用getAttribute
方法獲取Map,而後調用Map的clear方法清空數據。這裏只是爲了演示removeAttribute
方法的使用。
理論上講,會話能夠存儲但願存儲的數據。
固然,也必須考慮但願存儲數據的大小。若是在會話中添加了過多數據,那麼有可能會致使虛擬機的內存耗盡。
示例源碼(在會話中存儲更復雜的數據)連接:https://pan.baidu.com/s/1cLQS-qLiYd9LSOiksMCDFA 密碼:ed9f
Java EE中會話最有用的特性之一就是會話事件。
使用監聽器檢測會話的變化。Servlet API中定義了幾種監聽器,大多數儘管不是所有,都將監聽某種形式的會話活動。經過實現對應事件的監聽器接口訂閱某個事件,而後在部署描述符中添加<listener>
配置,或者在該類中添加註解@javax.servlet.annotation.WebListener
。
當某個事件發生時,將觸發事件的發佈,而後容器將調用對應事件監聽器中的方法。
@WebListener public class SessionListener implements HttpSessionListener, HttpSessionIdListener { private SimpleDateFormat formatter = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss"); @Override public void sessionCreated(HttpSessionEvent e) { System.out.println(this.date() + ": Session " + e.getSession().getId() + " created."); SessionRegistry.addSession(e.getSession()); } @Override public void sessionDestroyed(HttpSessionEvent e) { System.out.println(this.date() + ": Session " + e.getSession().getId() + " destroyed."); SessionRegistry.removeSession(e.getSession()); } @Override public void sessionIdChanged(HttpSessionEvent e, String oldSessionId) { System.out.println(this.date() + ": Session ID " + oldSessionId + " changed to " + e.getSession().getId()); SessionRegistry.updateSessionId(e.getSession(), oldSessionId); } private String date() { return this.formatter.format(new Date()); } }
或者在部署描述符中配置
<listener> <listener-class>com.wrox.SessionListener</listener-class> </listener>
示例源碼(使用會話)連接:https://pan.baidu.com/s/1r4Ojmj22MuXRA3EzIITdTA 密碼:5ejb