使用會話維持狀態 一、會話 為了實現關聯同一個用戶端的多個請求和這些請求之間數據的共用,需要用到會話,會話用於維持請求和請求之間的狀態。從伺服器的角度,當用戶的Web瀏覽器打開第一個鏈接到伺服器的套接字時請求就開始了,直到伺服器返回最後一個數據包並關閉鏈接是,該請求將結束。此時用戶瀏覽器和伺服器之間 ...
使用會話維持狀態
一、會話
為了實現關聯同一個用戶端的多個請求和這些請求之間數據的共用,需要用到會話,會話用於維持請求和請求之間的狀態。從伺服器的角度,當用戶的Web瀏覽器打開第一個鏈接到伺服器的套接字時請求就開始了,直到伺服器返回最後一個數據包並關閉鏈接是,該請求將結束。此時用戶瀏覽器和伺服器之間不再有任何的聯繫,當下一個鏈接開始時,無法將新的請求和之前的請求關聯起來。
維持狀態
最經典的例子就是線上購物網站需要用購物車來保證用戶和商品都能夠被保持。
記住用戶
這樣的例子是用戶論壇網站,在多個操作中,用戶只需要登錄一次。
啟動任務程式工作流
用戶在使用Web應用程式完成某個任務時,需要某種形式的工作流,比如新聞的發佈。
二、使用會話cookie和URL重寫。
會話是由伺服器或Web應用程式管理的某些文件、記憶體片段、對象或者容器,它包含了分配給它的各種不同數據。
通常會話被賦予一個隨機生成的字元串,稱為會話ID。第一次創建會話時,創建的會話ID會作為響應的一部分返回到用戶的瀏覽器中。接下來從該用戶瀏覽器發出的請求都將通過某種方式包含這個會話ID。當應用程式收到含有會話ID的請求時,它可以通過該ID將現有會話和當前請求關聯起來。
實現會話ID從伺服器返回到瀏覽器中的方法包括會話cookie和URL重寫。
會話cookie
這種技術也叫HTTP cookie。cookie是一種必要的通信機制,可以通過Set-Cookie響應頭在伺服器和瀏覽器中傳遞任意數據,並存儲在用戶電腦中,然後再通過請求頭Cookie從瀏覽器返回到伺服器中。cookie包含了功能變數名稱、路徑、過期日期或最大生命周期,安全標誌或只含有HTTP標誌。會話cookie的名字預設為JSESSIONID。
Domain將告訴瀏覽器應該將cookie發送到哪個功能變數名稱中,
Path則進一步將cookie限制在相對於域的某個特定的URL中,
Expries定義了cookie的絕對過期日期,
如果存在Secure特性,瀏覽器只會通過HTTPS發送cookie,進行加密傳輸,
HttpOnly將cookie限制在瀏覽器,避免JavaScript和flash。
URL中的會話ID
另一種傳輸會話ID的方式是通過URL,Web伺服器直到如何查找URL中包含會話ID的特定模式,不同的技術對如何在URL中內嵌和定位會話ID使用不同的策略。在Java EE中,會話ID被添加到URL的最後一個路徑段的舉證參數中,通過這種方式會分離開會話ID與查詢字元串的參數。例如:
http://www.example.com/supprot;JSESSIONID=NRxclGg2vG7kI4MdlLn?foo=bar
必須將會話ID內嵌在應用程式返回的所有URL中,包括頁面的鏈接、表單操作以及重定向。
HttpServletResponse介面定義了兩個重寫URL的方法:encodeURL和encodeRedirectURL,它們將在必要的時候把會話ID內嵌在URL中。
漏洞
複製粘貼錯誤,會話固定,跨站腳本和會話劫持,不安全的cookie
三、在會話中存儲數據
在部署描述符中配置會話:
<session-config> <session-timeout>30</session-timeout> <cookie-config> <http-only>true</http-only> </cookie-config> <tracking-mode>COOKIE</tracking-mode> </session-config>
所有的<session-config>和<cookie-config>標簽都是可選的,至於標簽的作用可以另行查看。使用上述配置,會話超時時間為30min,只接受cookie用於會話追蹤,。
存儲,刪除和獲取數據:
在Servlet中創建map,可以使用該Map來進行數據的相關操作。
private final Map<Integer, String> products = new Hashtable<>(); public StoreServlet() { this.products.put(1, "Sandpaper"); this.products.put(2, "Nails"); this.products.put(3, "Glue"); this.products.put(4, "Paint"); this.products.put(5, "Tape"); }
1.doGet方法中的使用:
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action = request.getParameter("action"); if(action == null) action = "browse"; switch(action) { case "addToCart": this.addToCart(request, response); break; case "emptyCart": this.emptyCart(request, response); break; case "viewCart": this.viewCart(request, response); break; case "browse": default: this.browse(request, response); break; } } 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"); } private void emptyCart(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getSession().removeAttribute("cart"); response.sendRedirect("shop?action=viewCart"); } private void viewCart(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setAttribute("products", this.products); request.getRequestDispatcher("/WEB-INF/jsp/view/viewCart.jsp") .forward(request, response); } private void browse(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setAttribute("products", this.products); request.getRequestDispatcher("/WEB-INF/jsp/view/browse.jsp") .forward(request, response); }
四、將使用會話的用戶群集化
集群為應用程式增加了冗餘和可擴展性,經過正確配置的群集應用程式即使在遇到某些伺服器終止時也能夠正常運行,是指在執行日常維護工作時也可以正常處理用戶請求。管理員甚至可以升級應用程式,並保證應用程式不會終止對請求的處理。
Advanced Message Queuing Protocol(AMQP)、Java Message Service(JMS)、Microsoft Message Queuing(MSMQ)。
問題:會話一對象的方式存在於記憶體中,並且只存在於Web容器的單個實例中,來自同一個客戶端的兩個連續請求將會訪問不同的Web容器,而第一個容器分配的ID,第二個容器無法識別。
解決:使用粘滯會話:使負載均衡機制能夠感知到會話,並且總是將來自於同一會話的請求發送到相同的伺服器。(取決於負載均衡技術,比如負載均衡器在響應中添加他們自己的會話cookie,併在後學的請求中識別這些cookie)。