全部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