Servlet 3.0 之 Sessions

超文本传输协议(HTTP)被设计为一个无状态协议。为了构建高效Web应用,必须要把来自某个特定客户端的请求与其它请求关联起来。过去针对会话追踪发展演变了很多策略,但是对于程序员而言它们都很难直接被使用。

这份规范定义了一个简单的 HttpSession 接口,它允许一个servlet容器使用几种方式的任意一种来追踪用户会话,而无需让应用开发者关注具体细节。

一、Session追踪机制

下列章节描述了追踪一个用户会话的几种方式。

  1. Cookies
    HTTP cookies是被应用得最广泛的会话跟踪机制,并且要求所有的servlet容器都支持它。

容器会发送一个cookie给客户端。然后客户端会在随后的每次请求中把cookie返回给服务器,清楚地给请求绑定一个会话。会话跟踪cookie的标准名字必须是JSESSIONID,这必须被所有兼容3.0的容器支持。容器允许会话跟踪cookie的名字通过容器具体配置来被定制。

所有servlet容器必须提供一种能力来配置容器是否把会话跟踪cookie标记为HttpOnly。已有的配置必须应用于所有还未建立上下文具体配置的上下文(参考SessionCookieConfig javadoc了解更多)。
如果一个web应用为它的会话跟踪cookie配置一个自定义名字,且会话id被编码在URL中(URL重写),这个名字将会被作为URI参数的名字。

  1. SSL会话
    Secure Sockets Layer(应用于HTTPS协议中的加密技术)拥有一个内建机制来允许来自同一个客户端的多个请求成为一个会话的一部分。
  2. URL重写
    URL重写是会话跟踪的最低通用标准。当一个客户端不接受一个cookie,URL重写可以被服务器用来作为会话跟踪的基础。URL重写把数据和一个会话ID添加到一个URL路径中,这个路径会被容器解析以便把请求与一个会话关联起来。

会话ID必须在URL字符串中被编码为一个路径参数。参数的名字必须是jsessionid。这是一个包含编码路径信息的一个URL示例:
http://www.myserver.com/catalog/index.html;jsessionid=1234
URL重写把会话标识符暴露在日志,书签,引用头部,缓存的HTML和URI块中。在cookies或者SSL会话可用的情况下,URL重写不应该被用作会话追踪机制。

  1. 会话完整性
    当服务来自不支持cookies的客户端的HTTP请求时,Web容器必须能够支持HTTP会话。

二、创建一个会话

当会话仅是一个可预见的会话且还没有被建立,那么它就被认为是新的。因为HTTP是一个基于请求应答的协议,一个HTTP会话被认为是新的,直到一个客户端加入进来。客户端在会话追踪信息被返回给服务器时加入会话,并且标识一个会话已经被建立。在客户端加入会话前,不能假设来自客户端的下一个请求将会被认为一个会话的一部分。

如果下列任意一条成立,那么会话被认为是新的:

  • 客户端还不知道会话
  • 客户端选择不加入会话

这些条件定义了这个情景-servlet容器没有把一个请求与之前的一个请求关联起来的机制。
一个Servlet开发者必须设计他的应用来处理客户端没有,不能或者不愿意加入一个会话的场景。

三、会话范围

HttpSession 对象必须是应用级范围(servlet上下文级别)。底层机制对不同的上下文都是一样的,比如用来建立会话的cookie,但是被引用的对象一定不允许被容器在上下文中共享,也包括那些对象中的属性。

用一个例子来说明这个要求:
如果一个servlet使用RequestDispatcher来调用其它Web应用的一个servlet,任何为正被调用的servlet创建以及可见的会话必须与那些调用servlet不同。

此外,一个上下文的会话必须被请求恢复到上下文中,无论它们关联的上下文是否正被直接访问,或者在会话创建时刻作为请求分发的目标。

四、绑定属性到一个会话中

一个servlet能够通过名字把一个对象属性绑定到一个HttpSession实现中。任何绑定到一个会话的对象对属于同一个ServletContext以及处理同一个会话中请求的其它servlet可用。

一些对象在它们被放进会话或者从一个会话中移除时,可能需要通知。这些信息可以通过让对象实现HttpSessionBindingListener 接口来获取。这个接口定义了下列方法来通知绑定到会话的对象,或者从会话解绑的对象:

  • valueBound
  • valueUnbound

方法valueBound必须在HttpSession 接口的getAttribute方法调用这个对象之前被调用。方法valueUnbound必须在HttpSession 接口的getAttribute方法不再使用这个对象之后被调用。

五、会话超时

在HTTP协议中,当一个客户端不再活跃时,并没有明显的结束信号。这意味着能用来标识一个客户端何时不再活跃的唯一机制是一个超时周期。
servlet容器为会话定义默认超时周期,并且能通过HttpSession 接口的getMaxInactiveInterval方法来获取这个默认值。开发者可以使用HttpSession 接口的setMaxInactiveInterval方法来修改这个超时时间。这些方法定义的超时时间单位都是秒。如果超时时间被设置为0或者更小的值,那么会话将永不过期。直到使用那个会话的所有servlets都退出service方法,会话才会失效。一旦一个会话失效,一个新的请求一定不能在访问到那个会话。

六、最后访问时间

getLastAccessedTime 接口的HttpSession方法允许一个servlet在当前请求之前决定会话被访问的最后时间。当属于一个会话的请求第一次被servlet容器处理的时候,会话就会认为被访问到。

七、重要的会话机制

  1. 线程问题
    多个servlets执行请求线程可能在同一时刻对相同的会话对象访问。容器必须确保代表会话属性的内部数据结构的操作在线程安全的方式下执行。开发者需要负责对属性对象本身的线程安全访问。这将阻止HttpSession对象内部的属性集合的并发访问,避免应用引起集合崩溃。

  2. 分布式环境
    在一个分布式应用中,一个会话中的所有请求必须在某一时刻被一个JVM处理。容器必须能够处理所有使用方法setAttributeputValue被放到HttpSession类的实例中的对象。下列限制强制用来满足这些条件:

  • 容器必须接收实现了Serializable 接口的类。
  • 容器可以选择支持HttpSession中其它特定对象的存储,比如企业JavaBeans组件和事务的引用。
  • 会话的迁移将被特定容器组件处理。

分布式servlet容器必须为那些存储它们的容器不能支持会话迁移机制的对象抛出一个IllegalArgumentException

分布式servlet容器必须为实现Serializable的迁移对象提供必要机制。
这些限制意味着开发者确保在一个非分布式容器遇到的并发问题之外,不会再有额外的并发问题。

容器提供者能够通过从分布式系统的任意活动节点移动一个会话对象和它的内容到系统的一个不同节点,来保证服务的可扩展性和质量。

如果分布式容器持续或者迁移会话来提供服务质量的特性,它们不会被限制使用本地JVM序列化机制来序列化HttpSessions和它的属性。如果开发者实现了readObjectwriteObject方法,他们并不保证容器将会调用会话属性上的readObjectwriteObject,但是它们的序列化闭包将会保留。

容器在会话迁移期间必须通知任意一个实现了HttpSessionActivationListener 的会话属性。他们必须在一个会话序列化之前通知不活跃的监听者以及在一个会话反序列化后通知活跃的监听者。

写分布式应用的开发者应该意识到,由于一个容器可以运行在多个虚拟机上,开发者不能够依赖静态变量来存储一个应用状态。他们应该使用企业级bean或者数据库存储这种状态。

客户端机制
由于cookie或者SSL认证通常由Web浏览器控制且不予浏览器任何特定窗口关联,来自一个客户端应用所有窗口的请求对于一个servlet可能是同一个会话的一部分。为了最大的可移植性,开发者应该认为一个客户端的所有窗口都属于同一个会话。

翻译自 Java Servlet Specification
Version 3.0 Rev a
Author:Rajiv Mordani
Date: December 2010

你可能感兴趣的:(Servlet 3.0 之 Sessions)