tomcat之session和cookie

关键字: tomcat之session和cookie大揭密
转载自
http://tech.it168.com/j/2007-09-29/200709291005796.shtml
作者:IT168 极地圣火  2007-09-29


一、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的超时时间:
Java代码
  1. Cookie cookie = new Cookie("key","value");  
  2. cookie.setMaxAge(3600);  // Cookie的超时间为3600秒,也就是1小时  
  3. response.addCookie(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文件:
Java代码
  1. Cookie cookie = new Cookie("key","value");  
  2. esponse.addCookie(cookie);  
Cookie cookie = new Cookie("key","value"); response.addCookie(cookie);


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

Java代码
  1. Cookie cookie = new Cookie("key","value");  
  2. cookie.setMaxAge(-100);  // 将cookie设为临时Cookie  
  3. response.addCookie(cookie);  
Cookie cookie = new Cookie("key","value"); cookie.setMaxAge(-100);  // 将cookie设为临时Cookie response.addCookie(cookie);


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

我们可以在JSP中使用如下的Java代码来输出Cookie字段的值:
Java代码
  1. out.println(request.getHeader("Cookie"));  
out.println(request.getHeader("Cookie"));

如果在Servlet中输出,必须得使用如下语句得到out,才能向客户端浏览器输出数据:
Java代码
  1. PrintWriter out = response.getWriter();  
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是如何工作 的。

让我们先假设某一个客户端第一次访问一个Servlet,在这个Servlet中使用了getSession来得到一个Session对象,也就是建立了一个会话,这个Servlet的代码如下:

Java代码 <embed height="15" width="14" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" allowscriptaccess="always" quality="high" flashvars="clipboard=import%20java.io.*%3B%0Aimport%20javax.servlet.ServletException%3B%0Aimport%20javax.servlet.http.*%3B%0Apublic%20class%20First%20extends%20HttpServlet%0A%7B%0A%20%20public%20void%20doGet(HttpServletRequest%20request%2C%20HttpServletResponse%20response)%0Athrows%20ServletException%2C%20IOException%0A%20%20%7B%0A%20%20%20%20%20response.setContentType(%22text%2Fhtml%22)%3B%0A%20%20%20%20%20HttpSession%20session%20%3D%20request.getSession()%3B%0A%20%20%20%20%20session.setAttribute(%22key%22%2C%20%22mySessionValue%22)%3B%0A%20%20%20%20%20PrintWriter%20out%20%3D%20response.getWriter()%3B%0A%20%20%20%20%20out.println(%22The%20session%20has%20been%20generated!%22)%3B%0A%20%20%20%20%20out.flush()%3B%0A%20%20%20%20%20out.close()%3B%0A%20%20%7D%0A%7D%0A%0A" src="http://zhangguoli1997.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf">
  1. import java.io.*;  
  2. import javax.servlet.ServletException;  
  3. import javax.servlet.http.*;  
  4. public class First extends HttpServlet  
  5. {  
  6.   public void doGet(HttpServletRequest request, HttpServletResponse response)  
  7. throws ServletException, IOException  
  8.   {  
  9.      response.setContentType("text/html");  
  10.      HttpSession session = request.getSession();  
  11.      session.setAttribute("key""mySessionValue");  
  12.      PrintWriter out = response.getWriter();  
  13.      out.println("The session has been generated!");  
  14.      out.flush();  
  15.      out.close();  
  16.   }  
  17. }  
import java.io.*; import javax.servlet.ServletException; import javax.servlet.http.*; public class First extends HttpServlet {   public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException   {      response.setContentType("text/html");      HttpSession session = request.getSession();      session.setAttribute("key", "mySessionValue");      PrintWriter out = response.getWriter();      out.println("The session has been generated!");      out.flush();      out.close();   } }

对于服务端的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响应头:
Java代码
  1. HTTP/1.1 200 OK  
  2. ...  
  3. Set-Cookie: JSESSIONID=D5A5C79F3C8E8653BC8B4F0860BFDBCD  
  4. ...  
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的代码如下:
Java代码 <embed height="15" width="14" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" allowscriptaccess="always" quality="high" flashvars="clipboard=import%20java.io.*%3B%0Aimport%20javax.servlet.ServletException%3B%0Aimport%20javax.servlet.http.*%3B%0Apublic%20class%20Second%20extends%20HttpServlet%0A%7B%0A%20%20%20%20public%20void%20doGet(HttpServletRequest%20request%2C%20HttpServletResponse%20response)throws%20ServletException%2C%20IOException%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20response.setContentType(%22text%2Fhtml%22)%3B%0A%20%20%20%20%20%20%20%20HttpSession%20session%20%3D%20request.getSession()%3B%0A%20%20%20%20%20%20%20%20PrintWriter%20out%20%3D%20response.getWriter()%3B%0A%20%20%20%20%20%20%20%20out.println(session.getAttribute(%22key%22))%3B%0A%20%20%20%20%20%20%20%20out.flush()%3B%0A%20%20%20%20%20%20%20%20out.close()%3B%0A%20%20%20%20%7D%0A%7D%0A" src="http://zhangguoli1997.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf">
  1. import java.io.*;  
  2. import javax.servlet.ServletException;  
  3. import javax.servlet.http.*;  
  4. public class Second extends HttpServlet  
  5. {  
  6.     public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException  
  7.     {  
  8.         response.setContentType("text/html");  
  9.         HttpSession session = request.getSession();  
  10.         PrintWriter out = response.getWriter();  
  11.         out.println(session.getAttribute("key"));  
  12.         out.flush();  
  13.         out.close();  
  14.     }  
  15. }  
import java.io.*; import javax.servlet.ServletException; import javax.servlet.http.*; public class Second extends HttpServlet {     public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException     {         response.setContentType("text/html");         HttpSession session = request.getSession();         PrintWriter out = response.getWriter();         out.println(session.getAttribute("key"));         out.flush();         out.close();     } }


如果在同一个窗口来调用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对象了。答案是肯定的。下面是新的First代码:

Java代码 <embed height="15" width="14" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" allowscriptaccess="always" quality="high" flashvars="clipboard=import%20java.io.*%3B%0Aimport%20javax.servlet.ServletException%3B%0Aimport%20javax.servlet.http.*%3B%0Apublic%20class%20First%20extends%20HttpServlet%0A%7B%0A%20%20%20%20public%20void%20doGet(HttpServletRequest%20request%2C%20HttpServletResponse%20response)throws%20ServletException%2C%20IOException%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20response.setContentType(%22text%2Fhtml%22)%3B%0A%20%20%20%20%20%20%20%20HttpSession%20session%20%3D%20request.getSession()%3B%0A%20%20%20%20%20%20%20%20session.setMaxInactiveInterval(3600)%3B%0A%20%20%20%20%20%20%20%20Cookie%20cookie%20%3D%20new%20Cookie(%22JSESSIONID%22%2C%20session.getId())%3B%0A%20%20%20%20%20%20%20%20cookie.setMaxAge(3600)%3B%0A%20%20%20%20%20%20%20%20response.addCookie(cookie)%3B%0A%20%20%20%20%20%20%20%20session.setAttribute(%22key%22%2C%20%22mySessionValue%22)%3B%0A%20%20%20%20%20%20%20%20PrintWriter%20out%20%3D%20response.getWriter()%3B%0A%20%20%20%20%20%20%20%20out.println(%22The%20session%20has%20been%20generated!%22)%3B%0A%20%20%20%20%20%20%20%20out.flush()%3B%0A%20%20%20%20%20%20%20%20out.close()%3B%0A%20%20%20%20%20%7D%0A%7D%0A" src="http://zhangguoli1997.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf">
  1. import java.io.*;  
  2. import javax.servlet.ServletException;  
  3. import javax.servlet.http.*;  
  4. public class First extends HttpServlet  
  5. {  
  6.     public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException  
  7.     {  
  8.         response.setContentType("text/html");  
  9.         HttpSession session = request.getSession();  
  10.         session.setMaxInactiveInterval(3600);  
  11.         Cookie cookie = new Cookie("JSESSIONID", session.getId());  
  12.         cookie.setMaxAge(3600);  
  13.         response.addCookie(cookie);  
  14.         session.setAttribute("key""mySessionValue");  
  15.         PrintWriter out = response.getWriter();  
  16.         out.println("The session has been generated!");  
  17.         out.flush();  
  18.         out.close();  
  19.      }  
  20. }  
import java.io.*; import javax.servlet.ServletException; import javax.servlet.http.*; public class First extends HttpServlet {     public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException     {         response.setContentType("text/html");         HttpSession session = request.getSession();         session.setMaxInactiveInterval(3600);         Cookie cookie = new Cookie("JSESSIONID", session.getId());         cookie.setMaxAge(3600);         response.addCookie(cookie);         session.setAttribute("key", "mySessionValue");         PrintWriter out = response.getWriter();         out.println("The session has been generated!");         out.flush();         out.close();      } }

在上面的代码中使用了Cookie对象将JSESSIONID写入了Cookie文件,并使用setMaxAge方法将Cookie超时时间设为3600 秒(1小时)。这样只要访问过First,从访问时间算起,在1小时之内,在本机的任何IE窗口调用Second都会得 到"mySessionValue"字符串。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值