HTTP是有一种无状态的协议,形象的说,就是大家在交流(发送/处理请求)的时候,无法识别谁在说话。
本文的前半部分会介绍基于CGI开发者使用的传统会话跟踪技术:用户鉴别、隐藏表单字段、URL重写和持续性cookie,后半段介绍Servlet API中对会话跟踪的内置支持。
本章主要介绍单服务器的状态,在《企业服务和J2EE》中将介绍多服务处理共享会话状态。
用户鉴别
在客户登陆进入后,servlet可以使用getRemoteUser()取得用户名。
我们可以使用用户名来跟踪客户端会话。用户登陆后,浏览器记住用户名并且在浏览新网页时重发名称和密码。serlvet通过用户名对用户进行鉴别并跟踪会话。举个简单的例子,一个用户在他的购物车上增加一项,这个信息就被记录(比如数据库中),以后用户结账的时候,另一个servlet调用数据库中的信息就可以了。
这个方法最大优点是容易实现。
最大的缺点是需要用户登陆账号,比较麻烦。另一个缺点是HTTP的基本鉴别没有提供注销技术,用户必须关闭浏览器以注销(当然我们现在都有登出操作了)。最后一个问题是,用户不能对一个网点进行多个会话。我们需要支持匿名会话跟踪和注销的鉴别技术。
隐藏的表单字段
支持匿名会话跟踪的一个方法是使用隐藏的表单字段。顾名思义,就是用HTML表当上不在客户端显示的字段,它们在表单提交后被返回到服务器。可以像这样使用它:
<FORM ACTION="/servlet/MovieFinder" METHOD="POST">
...
<INPUT TYPE=hidden NAME="zip" VALUE="94040">
<INPUT TYPE=hidden NAME="level" VALUE="expert">
...
</FORM>
在某种意义上,隐藏的表单字段为表单定义了一个常数常量。对于一个servlet而言,隐藏字段和可见字段没有区别。
隐藏表单字段的优点在于普遍性和对匿名的支持。隐藏字段被所有流行的浏览器支持,它们不需要服务器特殊服务,并且它们可以用于未注册和登陆的客户端。该技术的主要缺点是会话只能存在于动态生成的表单中。会话不能在静态文本、发送的文档、书签文档和在浏览器关闭时存在。
URL重写
URL重写是支持匿名会话跟踪的另一个方法。使用URL重写技术,每个可能被用户点击的本地URL都会被动态修改,或重写以包含附加信息。附加信息可能是附加路径信息、附加参数或定制的服务器端指定的URL变换形式。在URL重写中,附加信息通常是一个独特的会话ID。例如:
http://server:port/servlet/Rewritten original
http://server:port/servlet/Rewritten/123 extra path information
http://server:port/servlet/Rewritten?sessionid=123 added parameter
http://server:port/servlet/Rewritten;jsessionid=123 custom change
持续性cookie
cookie是一些由服务器发向浏览器并且还能由浏览器读回的信息。当浏览器收到一个cookie,就将这个cookie保存起来,并当每次它访问该服务器的网页时,都将这个cookie发送回服务器。因为一个cookie的值可以唯一确定一个客户端,所以cookie常用于会话跟踪。
使用cookie
servlet API提供了javax.servlet.http.Cookie类来运行cookie。cookie的HTTP首部有关细节处理由Servlet API进行。我们可以用Cookie()构造函数来生成一个cookie:
public Cookie(String name, String value)
通过在请求的防止中AddCookie()方法向客户端发送一个cookie,并且能用AddCookie()继续添加。浏览器对每个网站最多接受20个cookie,每个用户最多300个,并且浏览器将每个cookie设置在4096字节内。
public void HttpServletResponse.addCookie(Cookie cookie)
会话跟踪API(Session)
Servlet API提供了一些方法和类专门处理短期的会话跟踪。
会话跟踪基础
网站的每个用户都和javax.servlet.http.HttpSession对象有关,servlet使用这个对象来记录和检索每个用户的信息。
public HttpSession HttpServletRequest.getSession(boolean create)
会话的生命周期
会话并不是一直延续下去的。会话在非活动固定时间(Tomcat默认30分钟)后自动关闭,或者由servlet关闭。
设定会话超时
理论上讲,会话将在用户关闭浏览器时失效,连接到其他网站或用户离开。但是,服务器无法检测到这些事件。因此,服务器在会话不活动一段时间后就假设用户已经离开,不再需要保持会话了。
默认的会话超时可使用web.xml来自定义。
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
<!-- ..... -->
<session-config>
<session-timeout>
60 <!-- minutes -->
</session-timeout>
</session-config>
</web-app>
或者对HttpSession对象设置一个超时时间
public void HttpSession.setMaxInactiveInterval(int secs)
给出一个负值表示不会超时。
会话如何工作
当用户第一次访问一个Web应用程序,用户被分配一个HttpSession对象和一个唯一的会话ID。这个会话ID代表用户并用于和后边请求中的HttpSession相匹配。有些服务器使用一个会话ID,每个应用程序将这个ID映射到一个不同的HttpSession实例。有些服务器则会为每个Web应用程序分配一个会话ID,用来预防恶意入侵者模拟用户来破坏Web应用程序。
在后台,会话ID通常保存在客户端一个叫做JSESSIONID的cookie中。
会话绑定事件
一些对象希望在被绑定在会话和从会话中释放时都能有所动作。例如,一个数据库连接可能在绑定在会话上时开始一个传输,并在被释放时结束传输。任何实现了javax.servlet.http.HttpSessionBindingListener接口都从会话绑定或释放时被通知。这个接口实现了两个方法,valueBound()和valueUnbound()。