Session管理是Web应用程序开发中一个非常重要的主题,这是因为Web语言HTTP是无状态的。在默认情况下,Web服务器不知道一个HTTP请求时来自初次用户,还是来自之前访问过的用户。例如,webmail应用程序要求用户在查看邮件之前要先登录。但是,一旦用户输入正确的用户名和密码,用户在访问应用程序的其他部分时,就不应该提示他们再次登录。应用程序需要记住哪些用户已经登录成功。换句话说,它必须弄个管理用户Session。
保持状态的四种方法:
1.URL重写
2.表单隐藏域
3.cookie机制
4.HttpSession对象
1.URL重写
URL重写是一种Session追踪技术,需要将一个或多个token作为一个查询字符串添加到一个URL中。token的格式一般是键=值:
url?key1=value1&key2=value2&key3=value3&key4=value4...
注意,URL和token之间要用一个?隔开,两个token之间则用一个&符号隔开。
如果token不必在过多的URL中四处携带,那么URL重写就比较合适。但其也有缺点:
1.在有些文本浏览器中,URL限制为两千个字符;
2.仅当有链接要插入值时,值才能转换为后面的资源。此外,要把值添加到静态页面的链接中,可不是一件容易的事。
3.URL重写必须在服务器端有效。所有的链接都必须带有值,这样可能出现一个问题,即一个页面可能有多个链接。
4.某些字符,例如空格、&和问号都必须进行编码。
5.添加到URL中的信息是明显可见的,这种情况有时可能不是我们想要的。
由于上述局限性,URL重写只适用于那些既需要保持,却又不跨越太多页面,并且又不太重要的信息。
2.表单隐藏域
利用隐藏域来保持状态,与URL重写技术类似。但它不是将值添到URL后面,而是将它们放在HTML表单的隐藏域当中。当用户提交表单时,隐藏域中的值也传送到服务器。只有当页面包含表单时,或者可以在页面添加表单时,才适合使用表单域。这种技术胜过URL重写的地方在于,可以将更多的字符传到服务器,并且不需要进行字符编码。但跟URL重写一样,只有当传递的信息不需要跨越多个页面时,才适合使用这种技术。
3.cookie机制
URL重写和表单隐藏域只适用于那些不需要跨越太多页面的信息。如果这些信息需要跨越很多页面,这两种技术实现起来就比较困难,因为你必须要管理每个页面的信息。值得庆幸的是,cookie能够解决URL重写和表单隐藏域都无法解决的问题。
cookie是自动地在浏览器和服务器来回传递的一小块信息。cookie适用于那些需要跨越很多页面的信息。由于cookie是作为HTTP标头嵌入的,因此传输它的过程由HTTP协议处理。除此之外,还可以根据自己的需要设置cookie的有效期限。对于web浏览器而言每台web服务器最多可以支持20个cookie。
cookie的不足之处在于,用户可以通过修改他的浏览器设置来禁止cookie。
要使用cookie,必须熟悉javax.servlet.http.Cookie类,以及HttpServletRequest和HttpServletResponse中的几个方法。
要创建cookie,只需传递一个名称和值给Cookie的构造器:
Cookie cookie=new Cookie(name,value);
创建cookie之后,可以设置它的domain、path、maxAge属性。尤其值得关注的是maxAge属性,因为它决定了cookie的有效期限。为了将一个cookie发送到浏览器,需在HttpServletResponse上调用add方法:
httpServletResponse.add(cookie);
当浏览器再次发出对同一资源或者同一台服务器中的不同资源的请求时,它会同时把从web浏览器收到的cookie传回去。要访问浏览器发出的cookie,可以在HttpServletRequest中使用getCookies方法。该方法返回一个Cookie数组,如果请求中没有cookie,将返回null。为了找到某个名称的cookie,需要迭代数组,下面举例说明如何读取一个名为maxRecords的cookie:
Cookie[] cookies=request.getCookies();
Cookie maxRecordsCookie=null;
if(cookies!=null){
for(Cookie cookie:cookies){
if(cookie.getName().equals("maxRecords")){
maxRecordsCookie=cookie;
break;
}
}
}
令人遗憾的是,没有getCookieByName方法可以使获取cookie变得更简单一些。更令人难过的是,也没有方法直接删除cookie。为了删除cookie,需要创建一个同名coolie,将它的maxAge属性设为0,并在HttpServletResponse中添加一个新的cookie。举例删除一个名为userName的cookie:
Cookie cookie=new Cookie("userName","");
cookie.setMaxAge(0);
response.addCookie(cookie);
4.HttpSession对象
在所有Session追踪技术中,HttpSession对象是最强大的,也是功能最多的。用户可以没有或有一个HttpSession,并且只能访问他自己的HttpSession。
HttpSession是当一个用户第一次访问某个网站时自动创建的。通过在HttpServletRequeat中调用getSession方法,可以获取HttpSession。getSession有两个重载方法:
HttpSession getSession()
HttpSession getSession(boolean create)
无参的getSession方法返回当前HttpSession,如果当前没有,则创建一个并返回。getSession(false)方法返回当前的HttpSession(若有),如果没有,则返回null。getSession(true)和getSession()一样。
HttpSession的setAttribute方法签名如下:
void setAttribute(String name,Object obj)
注意,与前三种技术不同,放在HttpSession中的值是保存在内存中的。因此,你只能将尽可能小的对象放里面,并且数量不能太多。即使现代的Servlet容器可以在内存将满时将HttpSession中的对象移到辅助存储设备中,但是这样会影响性能。所以,对于保存在HttpSession中的内容一定要慎重。添加到HttpSession中的值可以使任意java对象,只要它的类实现了java.io.Serializable接口即可,以便当servlet容器认为有必要时,将其序列化成一个文件或保存到数据库中,例如,当容器的内存快要用完时。但仍可以将非序列化的对象保存在HttpSession中,但如果容器试图将它们序列化,将以失败告终,并抛出异常。setAttribute方法要求不同的对象有不同的名称。如果传递一个以前用过的的属性名称,那么该名称将与旧值无关联,而与新值相关联了。通过在HttpSession中调用getAttribute方法,同时传递一个属性名称,可以获取HttpSession中保存的对象。方法签名如下:
java.lang.Object getAttribute(java.lang.String name)
HttpSession中另一个有用的方法是getAttributeNames,它返回一个Enumeration,迭代一个HttpSession中的所有属性:
java.util.Enumeration
注意,HttpSession中保存的值不发送到浏览器,这与其他Session管理方法不同。而是Servlet容器为它所创建的HttpSession生成一个唯一的标识符,并将这个标识符作为一个token发送给浏览器,一般是作为一个名为JSESSIONID的cookie,或者作为一个jsessionid参数添加到URL后面。在后续的请求中,浏览器会将这个token发给服务器,使服务器能够知道是哪个用户在发出请求。无论servlet容器选择用哪一种方式传输session标识符,都是后台自动完成的,不需要你去做额外的处理工作。
通过在HttpSession中调用getId方法,可以获取HttpSession的标识符。
HttpSession中还定义了一个invalidate方法,这个方法强制session过期,并将绑定到它的所有对象解除绑定。在默认情况下,HttpSession是在用户静默一定时间之后过期。很多时候,还需要销毁未过期却又没用的HttpSession实例,以便释放一些内存空间。可以调用getMaxInactiveInterval方法,以了解一个HttpSession在用户最后一次访问之后还能维持多久。这个方法返回用户离开的秒数。setMaxInactiveInterval方法可以帮助你为个别HttpSession设置一个过期期限。
void setMaxInactiveInterval(int seconds)
如果将这个值设为0,则这个HttpSession将永不过期。一般来说,这不是一种好办法,因为HttpSession占用的堆空间将永不释放,直到应用程序卸载或servlet容器关闭为止。