jsp_cookies_转转转

本文详细讲解了在JSP与Servlet中Cookie和Session的工作原理及使用方法,包括如何利用Cookie保存数据并实现共享,Session的创建、维护及在不同场景下的应用。

JSP和Servlet中的Cookie

文章分类:Web前端

一、JSPServlet 中的Cookie  

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


    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 文件:

 
Cookie cookie = 
new
 Cookie("key","value");
response.addCookie(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 中的ServletSession

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

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


    
首先让我们先来看看Session 的原理。SessionCookie 类似。所不同的是它是建立在服务端的对象。每一个Session 对象一个会话。也许很多读者看到这会有一个疑问。Session 是如何同客户端联系在一起的呢?很多人在使用Session 时并没有感觉到这一点。其实这一切都是Web服务器 ,如Tomcat 一手包办的。那么Web服务器 又是如何识别通过HTTP 协议进行连接的客户端的呢?这就要用到第一节中所讲的Cookie 。在一般情况下,Session 使用了临时Cookie 来识别某一个Session 是否属于某一个会话。在本文中以Tomcat 为例来说明Session 是如何工作的。

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

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 字段中获得一个寻找一个JSESSIONIDkey ,这个key 的值是一个唯一字符串,类似于D5A5C79F3C8E8653BC8B4F0860BFDBCD 

    
2. 
如果Cookie 中包含这个JSESSIONID ,将key 的值取出,在TomcatSession 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 返回。接下来我们使用另外一个ServletSecond 来获得在First 中所设置的Session 数据。Second 的代码如下:

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 。因为这时SecondFirst 拥有了不同的Session ID ,因此,它们并不属于同一个会话。讲到这,也许很多读者眼前一亮。既然拥有同一个Session ID ,就可以共享Session 对象,那么我们可不可以使用永久Cookie 将这个Session ID 保存在Cookie 文件中,这样就算在新的IE 窗口,也可以共享Session 对象了。答案是肯定的。下面是新的First 代码:

 

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" 字符串。

 

三三、Tomcat 中的JSPSession


    
从本质上讲,JSP 在运行时已经被编译成相应的Servlet 了,因此,在JSPServletSession 的使用方法应该差不多。但还是有一些细小的差别。

    
如果我们使用过JSP 就会发现,在JSP 中很多对象是不需要创建的,如outsession 等。它们可以直接使用。如下面的JSP 代码所示:

<!-- MyJSP.jsp -->

< %@ page  language ="java"   contentType ="text/html; charset=GB18030"

     pageEncoding ="GB18030" % >

<! DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" >

< html >

     < head >

         < meta  http-equiv ="Content-Type"   content ="text/html; charset=GB18030">

         < title > Insert title here </ title >

     </ head >

     < body >

         < %

out .println(session.getId());

% >

     </ body >

</ html >

 

     在上面的JSP 代码中直接使用了outsession 。而并不象Servlet 里一样用get 方法来获得相应的对象实例。那么这是为什么呢?

    
由于JSP 在第一次运行时是被编译成Servlet 的,我们自然就会想到有可能是在编译JSP 时自动创建了sessionout 对象。下面我们就来验证这一点。首先需要查看一下JSP 被编译成Servlet 后的源代码。这非常简单,如果我们使用的是Tomcat ,只需要在Tomcat 的安装目录中的work 中找相应的源程序即可。如一个名为MyJSP.jsp 的文件首先被编译成MyJSP_jsp.java (这就是由JSP 生成的Servlet 源程序文件),然后再由javaMyJSP_jsp.java 编译成MyJSP_jsp.class ,最后Tomcat 运行的就是MyJSP_jsp.class 。如上面的JSP 程序被编译成Servlet 的部分源代码如下:

package org.apache.jsp;

import javax.servlet.*;

import javax.servlet.http.*;

import javax.servlet.jsp.*;

public   final  class   MyJSP_jsp extends org.apache.jasper.runtime.HttpJspBase

{

... ...

... ...

public   void   _jspService(HttpServletRequest request, HttpServletResponse response)

throws java.io.IOException, ServletException {

PageContext pageContext =  null ;

HttpSession session =  null ;

ServletContext application =  null ;

ServletConfig config =  null ;

JspWriter  out   =  null ;

Object page =  this ;

JspWriter _jspx_out =  null ;

PageContext _jspx_page_context =  null ;

try   {

response.setContentType("text/html; charset=GB18030");

pageContext = _jspxFactory.getPageContext( this , request, response,

null ,  true , 8192,  true );

_jspx_page_context = pageContext;

application = pageContext.getServletContext();

config = pageContext.getServletConfig();

session = pageContext.getSession();

out   = pageContext.getOut();

_jspx_out =  out ;

out .write("\r\n");

out .write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");

out .write("<html>\r\n");

out .write("\t<head>\r\n");

out .write("\t\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=GB18030\">\r\n");

out .write("\t\t<title>Insert title here</title>\r\n");

out .write("\t</head>\r\n");

out .write("\t<body>\r\n");

out .write("\t\t");

out .println(session.getId());

out .write("\r\n");

out .write("\r\n");

out .write("\t</body>\r\n");

out .write("</html>");

}  catch   (Throwable t) {

if   (!(t instanceof SkipPageException)){

out   = _jspx_out;

if   ( out   !=  null   &&  out .getBufferSize() != 0)

try   {  out .clearBuffer(); }  catch   (java.io.IOException e) {}

if   (_jspx_page_context !=  null ) _jspx_page_context.handlePageException(t);

}

}  finally   {

_jspxFactory.releasePageContext(_jspx_page_context);

}

}

}

 

     我们可以看到上面的代码中的_jspService 方法类似于HttpServlet 中的service 方法,在方法的开始部分首先建立了sessionapplicationout 等对象实例。然后将MyJSP.jsp 中的HTML 通过out 输出到客户端。我们要注意上面的黑体字的语句:out.println(session.getId()); JSP 编译器自动将JSP 中的<% ... %> 中包含的Java 代码原封不动地插入到_jspService 中。由于是在创建对象实例后插入,因此,就可以直接使用sessionout 等对象了。

    
如果我们想做进一步的实验,可以直接使用javac 来编译MyJSP_jsp.java ,为了方便其间,首先建立一个c.cmd 文件,它的内容如下:

 

javac -classpath

D:\tools\apache-tomcat-6.0.13\lib\servlet-api.jar;D:\tools\apache-tomcat-6.0.13\lib\jsp-api.jar;D:\tools\apache-tomcat-6.0.13\lib\annotations-api.jar;D:\tools\apache-tomcat-6.0.13\lib\catalina.jar;D:\tools\apache-tomcat-6.0.13\lib\jasper.jar;D:\tools\apache-tomcat-6.0.13\lib\el-api.jar %1

    
其中D:\tools\apache-tomcat-6.0.13tomcat 的安装目录,读者可以将其设为自己的机器上的tomcat 安装目录

    
在编译时可直接使用c  MyJSP_jsp.java 进行编译,这时tomcat 就直接运行我们编译生成的MyJSP_jsp.class 了。

    
从上面的代码我们还可以了解一点,在JSP 无论使用还是不使用session ,都会使用getSession 方法创建一个Session 对象,而Servlet 必须显式地调用才会建立Session 对象。

注:通过直接编译java 文件运行jsp ,需要清除一下tomcat 的缓存,一般需要重启一下tomcat

 

四、随心所欲使用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) 
tomcatcookie 支持关闭


如果我们只想使用url 来支持session ,可以直接将tomcatcookie 功能关闭。我们可

以修改conf 中的context.xml 文件,加入一个cookies="false" 即可,内容如下:

<!-- The contents of this file will be loaded for each web application -->

<Context cookies = "false">

... ...

... ...

</Context>

    
重启tomcat 后,就算客户端支持cookietomcat 也不会考虑HTT

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值