理清 Tomcat中Session和Cookie

来源:互联网 发布:厨房专用垃圾桶淘宝 编辑:程序博客网 时间:2024/05/20 03:38

一、JSP和Servlet中的Cookie 

    由于HTTP协议是无状态协议(虽然Socket连接是有状态的,但每次用HTTP协议进行数据传输后就关闭的Socket连接,因此,HTTP协议并不会保存上一次的状态),因此,如果要保存某些HTTP请求过程中所产生的数据,就必须要有一种类似全局变量的机制保证数据在不同的HTTP请求之间共享。这就是下面要讲的Session和Cookie。


    Cookie是通过将数据保存在客户端的硬盘(永久Cookie)或内存(临时Cookie)中来实现数据共享的一种机制。在Windows下,保存在这些Cookie数据的目录一般是C:/Documents and Settings/Administrator/Cookies。每一个Cookie有一个超时时间,如果超过了这个时间,Cookie将自动失效。可按如下方法来设置Cookie的超时时间:

Cookie cookie = new Cookie("key","value");cookie.setMaxAge(3600); // Cookie的超时间为3600秒,也就是1小时 response.addCookie(cookie);
如果不使用setMaxAge方法,Cookie的超时时间为-1,在这种情况下,Cookie就是临时Cookie,也就是说这种Cookie实际上并不保存在客户端硬盘上,而是保存在客户端内存中的。读者可以在JSP中运行如下代码,看看是否会在上面提到的保存cookie的目录中生成cookie文件:

实际上使用setMaxAge将超时时间设为任意的负数都会被客户端浏览器认为是临时
Cookie,如下面的代码将在客户端内存中保存一个临时Cookie:

Cookie cookie = new Cookie("key","value");cookie.setMaxAge(-100); // 将cookie设为临时Cookie response.addCookie(cookie);
如果第一次将Cookie写入客户端(不管是硬盘还是内存),在同一台机器上第二次访问
该网站的jsp页面时,会自动将客户端的cookie作为HTTP请求头的Cookie字段值传给服务端,如果有多个Cookie,中间用";"隔开。如下面的HTTP请求头所示:
 
GET /test/First.jsp HTTP/1.1
HOST:localhost
...
Cookie:key1=value1;key2=value2
...
...

    我们可以在JSP中使用如下的Java代码来输出Cookie字段的值:
     out.println(request.getHeader("Cookie"));
 
如果在Servlet中输出,必须得使用如下语句得到out,才能向客户端浏览器输出数据:

       PrintWriter out = response.getWriter();

    虽然永久Cookie和临时Cookie在第二次向服务端发出HTTP请求时生成Cookie字段,但它们还是有一定的区别的。永久Cookie在任意新开启的IE窗口都可以生成Cookie。而临时Cookie由于只保存在当前IE窗口,因此,在新开启的IE窗口,是不能生成Cookie字段的,也就是说,新窗口和旧窗口是不能共享临时Cookie的。使用重定向机制弹出的新窗口也无法和旧窗口共享临时Cookie。但在同一个窗口可以。如在一个IE窗口输入http://localhost:8080/test/first.jsp,向内存写入一个临时Cookie后,在同一个IE窗口输入http://localhost:8080/test/second.jsp,浏览器在向服务端发送HTTP请求时,自动将当前浏览器的临时Cookie(也就是first.jsp所创建的Cookie)和永久Cookie作为HTTP请求头的Cookie字段值发送给服务端。但是如果新启一个IE窗口,由于新IE窗口没有这个临时Cookie,因此,second.jsp只发送了保存在硬盘上的永久Cookie。
二、Tomcat中的Servlet和Session

    由于Cookie数存在保存在客户端,这样对于一些敏感数据会带来一些风险。而且Cookie一般只能保存字符串等简单数据。并且大小限制在4KB。如果要保存比较复杂的数据,Cookie可能显得有些不合适。基于这些原因,我们自然会想到在服务端采用这种类似Cookie的机制来存储数据。这就是我们这节要讲的会话(Session)。而在一个客户端和服务端的会话中所有的页面可以共享为这个会话所建立的Session。

    那么什么是会话呢?有很多人认为会话就是在一台机器上客户端浏览器访问某个域名所指向的服务端程序,就建立了一个客户端到服务端的会话。然后关闭客户端浏览器,会话就结束。其实这并不准确。

    首先让我们先来看看Session的原理。Session和Cookie类似。所不同的是它是建立在服务端的对象。每一个Session对象一个会话。也许很多读者看到这会有一个疑问。Session是如何同客户端联系在一起的呢?很多人在使用Session时并没有感觉到这一点。其实这一切都是Web服务器,如Tomcat一手包办的。那么Web服务器又是如何识别通过HTTP协议进行连接的客户端的呢?这就要用到第一节中所讲的Cookie。在一般情况下,Session使用了临时Cookie来识别某一个Session是否属于某一个会话。在本文中以Tomcat为例来说明Session是如何工作的。
对于服务端的First来说,getSession方法主要做了两件事:
    1. 从客户端的HTTP请求头的Cookie字段中获得一个寻找一个JSESSIONID的key,这个key的值是一个唯一字符串,类似于D5A5C79F3C8E8653BC8B4F0860BFDBCD 。
    
2. 如果Cookie中包含这个JSESSIONID,将key的值取出,在Tomcat的Session Map(用于保存Tomcat自启动以来的所有创建的Session)中查找,如果找到,将这个Session取出,如果未找到,创建一个HttpSession对象,并保存在Session Map中,以便下一次使用这个Key来获得这个Session。
 
在服务器向客户端发送响应信息时,如果是新创建的HttpSession对象,在响应HTTP
头中加了一个Set-Cookie字段,并将JSESSIONID和相应的值反回给客户端。如下面的HTTP响应头:
 
HTTP/1.1 200 OK
...
Set-Cookie: JSESSIONID=D5A5C79F3C8E8653BC8B4F0860BFDBCD
...

    对于客户端浏览器来说,并不认识哪个Cookie是用于Session的,它只是将相应的临时Cookie和永久Cookie原封不动地放到请求HTTP头的Cookie字段中,发送给服务器。如果在IE中首次访问服务端的First,这时在当前IE窗口并没有临时Cookie,因此,在请求HTTP头中就没有Cookie字段,所以First在调用getSession方法时就未找到JSESSIONID,因此,就会新建一个HttpSession对象。并在Set-Cookie中将这个JSESSIONID返回。接下来我们使用另外一个Servlet:Second来获得在First中所设置的Session数据。
如果在同一个窗口来调用Second。这时客户端已经有了一个临时Cookie,就是JSESSIONID,因此,会将这个Cookie放到HTTP头的Cookie字段中发送给服务端。服务端在收到这个HTTP请求时就可以从Cookie中得到JSESSIONID的值,并从Session Map中找到这个Session对象,也就是getSession方法的返回值。因此,从技术层面上来说,所有拥有同一个Session ID的页面都应该属于同一个会话。

    如果我们在一个新的IE窗口调用Second,并不会得到mySessionValue。因为这时Second和First拥有了不同的Session ID,因此,它们并不属于同一个会话。讲到这,也许很多读者眼前一亮。既然拥有同一个Session ID,就可以共享Session对象,那么我们可不可以使用永久Cookie将这个Session ID保存在Cookie文件中,这样就算在新的IE窗口,也可以共享Session对象了。
 
在上面的代码中使用了Cookie对象将JSESSIONID写入了Cookie文件,并使用setMaxAge方法将Cookie超时时间设为3600秒(1小时)。这样只要访问过First,从访问时间算起,在1小时之内,在本机的任何IE窗口调用Second都会得到"mySessionValue"字符串。

四、随心所欲使用Session

(1) 使用url传递session id

    在上面讲过,在默认情况下session是依靠客户端的cookie来实现的。但如果客户端浏览器不支持cookie或将cookie功能关闭,那就就意味着无法通过cookie来实现session了。在这种情况下,我们还可以有另一种选择,就是通过url来传递session id。

    对于Tomcat来说,需要使用jsessionid作为key来传递session id。但具体如何传呢?可能有很多人认为会是如下的格式:

http://localhost:8080/test/MyJSP.jsp?jsessionid= D5A5C79F3C8E8653BC8B4F0860BFDBCD

    但实验上面的url并不好使。其实最直接的方法我们可以看一下Tomcat的源程序是如何写的,首先下载tomcat的源程序,然后找到CoyoteAdapter.java文件,并打开。在其中找到parseSessionId方法,这个方法是用来从url中提取Session id的。我们可以不必了解这个方法的全部代码,只看一下开头就可以。代码片段如下:

     ByteChunk uriBC = req.requestURI().getByteChunk();
     int semicolon = uriBC.indexOf(match, 0, match.length(), 0);
     if (semicolon > 0) {...}

    上面代码中的uriBC就是请求的url,第二行在这个url中查找match字符串,再在CoyoteAdapter.java中查找一个match字符串,match变量的初值如下:

    private static final String match =
        ";" + Globals. SESSION_PARAMETER_NAME + "=";

    从上面代码可以看出,match开头是一个";"字符,而SESSION_PARAMETER_NAME是一个常量,值就是"jsessionid",因此可以断定,MyJSP.jsp后跟的是";",并不是"?",因此,正确的url如下:
 
http://localhost:8080/test/MyJSP.jsp;jsessionid= D5A5C79F3C8E8653BC8B4F0860BFDBCD

    通过使用上述方法甚至可以在不同的机器上获得同一个session对象。

    在CoyoteAdapter.java文件中还有一个parseSessionCookiesId方法,这个方法将从HTTP请求头中提取session id。我们中postParseRequest方法中可以看到将调用的parseSessionId方法,在最后调用了parseSessionCookiesId方法,因此,我们可以断定,tomcat将考虑url中的session id,然后再读取Cookie字段中的session id。还有就是在postParseRequest方法的最后部分有一个response.sendRedirect(redirectPath);,在调完它后,就直接return了。而没有执行到parseSessionCookiesId,因此,使用重定向并不能通过HTTP头的cookie字段共享session。只能通过url来传递session id。

(2) 将tomcat的cookie支持关闭

如果我们只想使用url来支持session,可以直接将tomcat的cookie功能关闭。我们可
以修改conf中的context.xml文件,加入一个cookies="false"即可,内容如下:

<!-- The contents of this file will be loaded for each web application -->
<Context cookies = "false">
... ...
... ...
</Context>

    重启tomcat后,就算客户端支持cookie,tomcat也不会考虑HTTP请求头的cookie字段。
连接http://blog.csdn.net/autoinspired/archive/2008/07/16/2662229.aspx

 

原创粉丝点击