浅谈session会话跟踪


什么是session呢?了解session之前,先要简单了解http协议。
http协议是面向无连接的协议,一次请求,一次操作,然后就结束了,是没有状态的。就是说,用户请求一次,server响应一次,完了它就翻脸不认人了。
然而实际上用户往往需要多次请求服务器才能完成一次“操作事件”,比如购物车,你先要请求server,浏览一下物品清单,然后要把物品放进购物车,最后再支付,至少需要和server交互3次以上,并且这3次请求,server都要知道是来自同一个用户。怎么办呢?使用session会话跟踪。

网上有很多文章说,Servlet/Jsp的会话管理技术主要有4种:URL重写,Cookie,隐藏表单域以及HttpSession。这个HttpSession不就是session是j2ee的实现规范嘛,怎么算一种会话管理技术?我搞不懂。。。
我看了j2ee 1.4 编程指南,传统上通常有3种会话跟踪:Cookie、隐藏表单和URL重写。
还有一种基于https的SSL会话,SSL(Secure Sockets Layer,安全套接字层)是HTTPS协议使用的一种加密技术,内建了会话跟踪功能,Servlet容器可以非常容易的使用这些数据建立会话跟踪。(但是HTTPS不是规范要求Servlet必须支持的协议) 因为HTTP是一种基于请求响应的协议,因此会话只有在客户端加入它以后才被新建立。当会话跟踪信息被成功的返回给服务器以指示会话给建立时客户端才算加入了一个会话。如果客户端没有加入会话,那么下一次请求不会被认为是会话的一部分。如何客户端还不知道会话或者客户端选择不加入一个会话,那么会话被认为是新的。开发者必须自己设计自己的应用中的会话处理状态,在什么地方没有加入会话,什么地方不能加入会话以及什么地方不需要加入会话。

重点说Cookie、隐藏表单和URL重写:
Cookie是用来干什么的?
Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器。
规范要求所有的Servlet都必须支持cookie。一般默认使用Cookie实现会话跟踪(这个要注意,我发现大部分项目都没有专门重写URL去实现会话跟踪,包括很多大型网站,像baidu贴吧,新浪微博,甚至是Google)。
关于是否应当使用cookie有很多的争论,因为一些人认为cookie可能会造成对隐私权的侵犯(当然事实上尚未发现)。有鉴于此,大部分浏览器允许用关闭cookie功能,然而这使我们跟踪会话变得更加困难。如果不能依赖cookie的支持又该怎办呢?

j2ee guide上提到了一个利用隐藏表单的方法,就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如下面的表单
<form name="testform" action="/xxx">
<input type="text">
</form>
在被传递给客户端之前将被改写成
<form name="testform" action="/xxx">
<input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
<input type="text">
</form>
这种技术现在已较少应用,知道有这么回事就OK了。

还有一种比较可行的办法是重写URL(URL Rewriting),就是在所有的页面跳转上加上 encodeURL() 或者 encodeRedirectURL(),容器会自动在请求url路径的后面追加上jsessionid=xxxxxxxxxxxxxxx。为了在整个交互过程中始终保持状态(不脱离会话),就必须在每个客户端可能请求的路径后面都包含这个session id,即每次请求都要重写url。

可以自己做一个简单的小例子来验证一下:
1.建一个web工程
2.在WebRoot下面,加入两个jsp
index.jsp

<%@ page language="java" import="java.util.*" pageEncoding="GBK"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'index.jsp' starting page</title>
  </head>
  
  <body>
			This is a index.jsp<br>
			Session Id : <%=session.getId()%><br>
			<a href="test.jsp">to test.jsp</a>
  </body>
</html>
test.jsp
<%@ page language="java" import="java.util.*" pageEncoding="GBK"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'test.jsp' starting page</title>
  </head>
  
  <body>
			This is a test.jsp<br>
			Session Id : <%=session.getId()%><br>
			<a href="index.jsp">to index.jsp</a>
  </body>
</html>
启动服务后,访问index.jsp,如下:

click to test.jsp,如下:


我们看到,上面并没有重写URL,也没有隐藏域放置jsessionid,但是两次请求在同一个会话里,原因就是Cookie在起作用。
下面来看看禁用Cookie之后的情况,设置IE8:工具->internet选项->隐私->设置->将滑轴拉到最顶上(阻止所有Cookies)。

访问index.jsp:

点击进入test.jsp:

可以看到两次访问server都重新生成了session,并不在同一会话里。

这时可以对URL进行重写,利用encodeURL()或encodeRedirectURL()方法。

将index.jsp中的<a href="test.jsp">to test.jsp</a>  改为  
<a href="<%=response.encodeRedirectURL("test.jsp")%>">to test.jsp</a>

将test.jsp中的<a href="index">to index.jsp</a>  改为  
<a href="<%=response.encodeRedirectURL("index.jsp")%>">to index.jsp</a>

在禁用cookies的情况下再次测试,发现两次情况状态一致,属于同一个session。


可能有人疑问,这个session和使用Hibernate时的session有什么关系呢?
hibernate的session是其持久化管理的核心,有着众多的的持久化方法,如save、update、delete、load等。
web层的session,也就是我们上面讲的session,只是一个服务器端用来保存信息的类似散列表的一个数据结构。
所以,上面两个session没有任何关系,除了名字一样。



收集的网上一些关于HttpSession常见问题
1.一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <% @page session="false"%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句 HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的 session对象的来历。
由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。

2.Session删除的时间是:
1)Session超时:超时指的是连续一定时间服务器没有收到该Session所对应客户端的请求,并且这个时间超过了服务器设置的Session超时的最大时间。
2)程序调用HttpSession.invalidate()
3)服务器关闭或服务停止


3.session的id是从哪里来的,sessionID是如何使用的:当客户端第一次请求session对象时候,服务器会为客户端创建一个session,并将通过特殊算法算出一个session的ID,用来标识该session对象,当浏览器下次(session继续有效时)请求别的资源的时候,浏览器会偷偷地将sessionID放置到请求头中,服务器接收到请求后就得到该请求的sessionID,服务器找到该id的session返还给请求者(Servlet)使用。一个会话只能有一个session对象,对session来说是只认id不认人


4.如何做到在浏览器关闭时删除session
严格的讲,做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力


5.跨应用程序的session共享
常常有这样的情况,一个大项目被分割成若干小项目开发,为了能够互不干扰,要求每个小项目作为一个单独的web应用程序开发,可是到了最后突然发现某几个小项目之间需要共享一些信息,或者想使用session来实现SSO(single sign on),在session中保存login的用户信息,最自然的要求是应用程序间能够访问彼此的session。

然而按照Servlet规范,session的作用范围应该仅仅限于当前应用程序下,不同的应用程序之间是不能够互相访问对方的session的。各个应用服务器从实际效果上都遵守了这一规范,但是实现的细节却可能各有不同,因此解决跨应用程序session共享的方法也各不相同。

首先来看一下Tomcat是如何实现web应用程序之间session的隔离的,从 Tomcat设置的cookie路径来看,它对不同的应用程序设置的cookie路径是不同的,这样不同的应用程序所用的session id是不同的,因此即使在同一个浏览器窗口里访问不同的应用程序,发送给服务器的session id也可以是不同的。


6.访问*.html的静态资源因为不会被编译为Servlet,也就不涉及session的问题


7.有个HttpSessionListener是怎么回事
你可以创建这样的listener去监控session的创建和销毁事件,使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener,而不是相反。类似的与HttpSession有关的listener还有 HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener


以上这些内容在j2ee 编程指南中都有比较详细的讲解,而且有大量的servlet例子,推荐初学者看这本书。
HttpSession的常见问题摘自其它网友的博客,
包括http://lavasoft.blog.51cto.com/62575/275589,http://www.yuanma.org/data/2006/1128/article_1872.htm。

智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告