Java Web 开发 四种 会话跟踪

一、会话跟踪的需求
HTTP是“无状态”协议:客户程序每次读取 Web 页面,都打开到 Web 服务器的单独的连接,并且,服务器也不自动维护客户的上下文信息。即使那些支持持续性 HTTP 连接的服务器,尽管多个客户请求连续发生且间隔很短时它们会保持 socket 打开,但是,它们也没有提供维护上下文信息的内建支持。上下文的缺失引起许多困难。例如,在线商店的客户向他们的购物车中加入商品时,服务器如何知道购物车中己有何种物品呢?类似地,在客户决定结账时,服务器如何能确定之前创建的购物车中哪个属于此客户呢?这些问题虽然看起来十分简单,但是由于 HTTP 的不足,解答它们却异常复杂困难。对于这个问题,存在 3 种典型的解决方案:
cookie
可以使用 cookie 存储购物会话的 ID;在后续连接中,取出当前的会话 ID,并使用这个 ID 从服务器上的查找表(lookup table)中提取出会话的相关信息。 以这种方式使用 cookie 是一种绝佳的解决方案,也是在处理会话时最常使用的方式。但是,sevlet 中最好有一种高级的 API 来处理所有这些任务,以及下面这些冗长乏味的任务:从众多的其他cookie中(毕竟可能会存在许多cookie)提取出存储会话标识符的 cookie;确定空闲会话什么时候过期,并回收它们;将散列表与每个请求关联起来;生成惟一的会话标识符。
URL 重写
采用这种方式时,客户程序在每个URL的尾部添加一些额外数据。这些数据标识当前的会话,服务器将这个标识符与它存储的用户相关数据关联起来。 URL重写是比较不错的会话跟踪解决方案,即使浏览器不支持 cookie 或在用户禁用 cookie 的情况下,这种方案也能够工作。URL 重写具有 cookie 所具有的同样缺点,也就是说,服务器端程序要做许多简单但是冗长乏味的处理任务。即使有高层的 API 可以处理大部分的细节,仍须十分小心每个引用你的站点的 URL ,以及那些返回给用户的 URL。即使通过间接手段,比如服务器重定向中的 Location 字段,都要添加额外的信息。这种限制意味着,在你的站点上不能有任何静态 HTML 页面(至少静态页面中不能有任何链接到站点动态页面的链接)。因此,每个页面都必须使用 servlet 或 JSP 动态生成。即使所有的页面都动态生成,如果用户离开了会话并通过书签或链接再次回来,会话的信息也会丢失,因为存储下来的链接含有错误的标识信息。
隐藏的表单域
HTML 表单中可以含有如下的条目:<input type="hidden" name="session" value="a1234">
这个条目的意思是:在提交表单时,要将指定的名称和值自动包括在 GET 或 POST 数据中。这个隐藏域可以用来存储有关会话的信息,但它的主要缺点是:仅当每个页面都是由表单提交而动态生成时,才能使用这种方法。单击常规的超文本链接并不产生表单提交,因此隐藏的表单域不能支持通常的会话跟踪,只能用于一系列特定的操作中,比如在线商店的结账过程。
二、servlet 的会话跟踪
servlet 提供一种出色的会话跟踪解决方案:HttpSession API 这个高层接口构筑在 cookie 或 URL 重写之上。所有的服务器都支持使用 cookie 的会话跟踪,大多数服务器可以全局地切换到 URL 重写。不管采用哪种方式,servlet 的开发设计人员都不需要为许多具体的实现细节操心,也不需要显式地操作 cookie 或附加到 URL 上的信息;他们自动获得一个区域,可以方便地存储与每个会话相关联的任意对象。
在 servlet 中,会话的使用比较简单直接,它涉及4个基本的步骤:
访问与当前请求相关联的会话对象。
调用 request.getSession 获取 HttpSession 对象,该对象是一个简单的散列表,用来存 储用户的相关数据。
查找与会话相关联的信息。
调用 HttpSession 对象的 getAttribute 将返回值转换成恰当的类型,并检查结果是否为null
存储会话中的信息。
使用 setAttribute,设置需要存储的值以及相应的键。
废弃会话数据。
调用 removeAttribute 废弃指定的值。调用 invalidate 废弃整个会话。调用 logout 使客户退出 Web 服务器,并作废与该用户相关联的所有会话。
下面对 HttpSession 类中的方法进行汇总:
public Object getAttribute(String name)
这个方法从会话对象中提取出之前存储的值。如果没有值与给定的名称相关联,它返回 null
public Enumeration getAttributeNames()
这个方法返回会话中所有属性的名称。
public void setAttribute(String name, Object value)
这个方法将值和名称关联起来。如果提供给 setAttribute 的对象实现了 HttpSessionBindingListener 接口,在它存储到会话中之后,它的 valueBound 方法 会被调用。类似地,如果之前的值实现了 HttpSessionBindingListener,它的 valueUnbound 方法会被调用。
public void removeAttribute(String name)
这个方法移除与指定名称关联的任何值。如果要移除的值实现了 HttpSessionBindingListener 接口,它的 valueUnbound 方法会被调用。
public void invalidate()
这个方法将会话作废,并释放所有与之相关联的对象。使用这个方法时要小心: 要牢记会话是与用户(即客户程序)相关联的,而不是单个的 servlet 或 JSP 页面。 因此,如果作废一个会话,有可能会破坏掉其他 servlet 或 JSP 页面正在使用的数据。
public void logout()
这个方法将客户从 Web 服务器中注销,并将与该客户相关联的所有会话作废。logout 的影响范围和身份验证的影响范围相同。例如,如果服务器实现了单一登 录,那么调用 logout 则会将客户从服务器上的所有 Web 应用中注销,并且将与该 客户相关联的所有会话(每个 Web 应用至多一个)作废。
public String getId()
这个方法返回每个会话所对应的惟一标识符。这对于调试或记录,或少数情况下 用编程的手段将值从内存移到数据库中(尽管某些 J2EE 服务器可以自动完成这项任务),都很有用。
public boolean isNew()
如果会话尚未和客户程序(浏览器)发生任何联系,则这个方法返回 true,这一般是 因为会话是新创建的,不是由输入的客户请求所引起。对于预先存在的会话,该 方法返回 false,提及这个方法的主要原因是尽量避免使用它。
public long getCreationTime()
这个方法返回会话首次构建的时间,格式为自1970年1月1日午夜(GMT)以来的 毫秒数。如果要获取用于打印的值,可以将该值传递给 Date 的构造函数或 GregorianCalendar 的 setTimeInMillis 方法。
public long getLastAccessedTime()
这个方法返回会话最后被客户程序访问的时间,格式为自1970年1月1日午夜(GMT)以来的毫秒数。
public int getMaxInactiveInterval()
public void setMaxInactiveInterval(int seconds)
这些方法读取或设置在没有访问的情况下,会话在被自动废弃之前应该保持多长 时间,以秒为单位。负值表示会话从不超时。要注意,超时由服务器来维护,它 不同于 cookie 的失效日期。首先,会话一般基于驻留内存的 cookie,不是持续性 cookie,因而也就没有截止日期。即使截取到 JSESSIONID cookie,并为它设定一个失效日期发送出去,浏览器会话和服务器会话也会截然不同。
三、对 URL 编码
默认地,servlet 容器(引擎)使用 cookie 作为会话跟踪的底层机制。但如果服务器进行了重新配置,使用 URL 重写(URL rewrite),则代码需要更改。
核心的会话跟踪代码不需做任何改动。其他大部分代码都需要进行更改。
特别地,如果页面中含有指向自身所在站点的链接时,必须显式地将会话数据添加到 URL。servlet API 提供将这项信息添加到任意指定 URL 的方法。问题是必须调用这些方法;如果想让系统检查所有 servlet 和 JSP 页面的输出,找出哪些部分包含指向自身站点的超链接并修改这些 URL,这在技术上是不可行的。这项需求表示:如果使用 URL 重写进行会话跟踪,那么就不能拥有任何静态 HTML 页面,或者至少不能拥有引用自身站点的静态 HTML 页面。这对于很多应用都是一项沉重的负担,但少数情况下是值得的。
两种情况下可能会用到引用自身站点的URL:
第一种情况是在 servlet 生成的 Web 页面中含有嵌入的 URL。这种情况下,应该将这些 URL 传递给 HttpServletResponse 的 encodeURL 方法。这个方法确定当前是否在使用 URL 重写,仅在必需时附加会话信息;否则,不做任何更改直接返回传入的URL。下面是一个具体的例子:
String originalURL = someRelativeOrAbsoluteURL;
String encodedURL = response.encodeURL(originalURL);
out.println("<a href="" + encodedURL + "">...</a>");

第二种情况是在 sendRedirect 调用中(即放入 Location 响应报头)。这种情况下,由于要根据不同的规则确定是否需要附加会话信息,因而不能使用 encodeURL。幸运的是,HttpServletResponse 提供 encodeRedirectURL 方法来处理这种情况。下面是一个具体的例子:
String originalURL = someURL;
String encodedURL = response.encodeRedirectURL(originalURL);
response.sendRedirect(encodedURL);

如果认为自己的 Web 应用最终有可能使用 URL 重写来替代 cookie,那么最好预先规划,对引用自身站点的 URL 进行编码。

本文转自:http://blog.sina.com.cn/s/blog_5ec67df20100gttl.html
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值