- 浏览: 457118 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (229)
- gef (1)
- emf (0)
- rcp (2)
- 杂谈 (3)
- draw2d (2)
- xml (1)
- spring (16)
- osgi (1)
- jsp (6)
- hibernate (8)
- j2se (41)
- oracle (25)
- js (23)
- ognl (1)
- struts2 (2)
- webwork (1)
- prototype (1)
- dwr (3)
- struts (7)
- axis2 (3)
- axis1 (6)
- lucene (9)
- pop3 (1)
- aspectj (1)
- 网络协议 (6)
- bat (6)
- Quartz (5)
- jms (3)
- jndi (7)
- 网络爬虫 (7)
- acegi (1)
- linux (5)
- 缓存 (1)
- mysql (1)
- 在使用Java处理图形应用时,经常有人推荐设置 -Djava.awt.headless=true,具体含义和效果查了一下,记录在这里分享 Headless模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。 Headless模式虽然不是我们愿意见到的,但事实上我们却常常需要在该模式下工作,尤其是服务器端程序开发者。因为服务器(如提供Web服务的主机)往往可能缺少前述设备,但又需要使用他们提供的功能,生成相应的数据,以提供给客户端(如浏览器所在的配有相关的显示设备、键盘和鼠标的主 (1)
最新评论
-
hanmiao:
注释掉的那壹行少了壹個斜线,楼主...
servlet导出excel -
天下无贼:
Thread.Interrupt方法,只是通过扔出异常的方式, ...
Java Thread.interrupt 害人! 中断JAVA线程(zz) -
天下无贼:
呵呵,是你自己写错了。
Java Thread.interrupt 害人! 中断JAVA线程(zz) -
MO_ZHUANG_D:
如果是真的就感激不尽了
Axure RP教程 -
小嘴冰凉:
在开始执行的时候,如果是数据库存储,程序会从数据库中查job信 ...
quartz的持久化
大多数的 Web 应用程序都要求维护某种会话状态,如用户购物车的内容。这种会话状态的保持很多情况下需要借助于Cookie或者Session的帮助。本文结合在线页面翻译 (Machine Translation System)项目中对于Cookie的处理方法,探讨一下如何在HTTP应用代理中正确处理Cookie的传递和管理问题。
读者定位为具有Java和Web开发经验的开发和设计人员。
读者可以学习到关于Cookie的工作原理和Cookie协议的细节,以及在一个HTTP应用代理的场景下Cookie的管理和处理思想,并可以直接使用文中的代码和思路,提高工作效率。
随着越来越多的系统移植到了Web上,HTTP协议具有了比以前更广泛的应用。不同的系统对WEB实现提出了不同的要求,基于HTTP协议的网络应用正趋于复杂化和多元化。很多应用需要把用户请求的页面进行处理后再返回给用户,比如页面关键字过滤,页面内容缓存、内容搜索、页面翻译等等。这些应用在实际效果上类似于一个HTTP应用代理:它们首先接受用户的请求,根据用户请求的URL去真正的目标服务器取回目标页面,再根据不同应用的要求做出相应处理后返回给用户。这样用户直接面对的就是这个HTTP应用代理,而通过它与其他页面进行交互。Cookie或Session技术的应用,解决了HTTP协议的一个问题 -- 无法保持客户状态,因此它现在被广泛应用于各种Web站点中。上面提到的那些应用如果不能处理好Cookie和Session的传递、更新和废除等问题,就会极大的限制它们所能处理站点的范围,因此如何在HTTP应用代理中正确处理Cookie,成为一个必须解决的问题。本文结合在页面翻译(Machine Translation System)项目中对于Cookie的处理方法,探讨一下这方面的解决方案。
MTS项目简介及讨论前提
Machine Translation System(以下简称MTS)是一个在线实时页面翻译系统,为用户在线提供把英文页面翻译成其他9种语言的服务。用户通过向MTS系统提交一个类似下面的URL使用此服务,其中参数url指明了用户所需要翻译的目标地址,参数language指明了所需翻译成的目标语言,www.mts.com是假想中提供MTS服务的站点。
HTTP://www.mts.com/translate?url=http://www.ibm.com/&language=French
一个完整的MTS系统处理过程可以分解成以下几个步骤:
用户向MTS提交合适的URL。
MTS在接到用户的请求后,解析出用户需要翻译的目标地址和目标语言,根据用户请求的目标地址,把请求转发到目标服务器。
MTS接受来自目标服务器的应答,包括页面信息和HTTP头信息。
MTS在确定得到正确的目标页面后,把页面内容送入WebSphere Translation Server进行翻译。
把翻译后的页面连同修改后的HTTP头信息提交给用户。
MTS逻辑图
当然,这其中涉及到很多的应用处理。比如与各种HTTP/HTTPS站点建立联结、根据HTTP头信息进行页面跳转和错误处理、为始终保持用户在翻译模式下而对目标的HTML页面进行分析和修改,根据系统设置对某些DNT(Do Not Translate)的页面进行过滤和跳转,当然还有对Cookie的处理等等。其他问题跟这篇文章关联不大,我们重点讨论在这种情况下的Cookie处理。Cookie跟随目标服务器的HTTP头信息被MTS接收到,经过MTS整理之后发给客户端浏览器。MTS在接到下一次用户对同一个站点的翻译请求时,再把从客户端得到的Cookie发送给目标服务器。
在以上的场景中,MTS充当的作用类似于一种HTTP应用代理服务器,它代替用户取得目标页面,并在作出相应处理后再提交给用户。当然,这种代理服务器不需要用户修改浏览器的代理服务器参数或者网络配置,而只是简单的在浏览器的地址栏中输入一个MTS能够识别的URL即可。此篇文章也是在这样一个应用场景的基础上,展开对HTTP应用代理服务器如何处理Cookie的讨论。
回页首
问题的产生
在MTS系统中,目标服务器的Cookie在两个地方会产生问题。当MTS接收目标服务器应答的时候,Cookie随着HTTP头信息被MTS接收到的。这时候目标服务器认为MTS就是最终客户,因此它赋予了Cookie与目标服务器相符的属性。而如果MTS把这些Cookie原封不动的保存在HTTP头信息中,传给真正的最终用户的话,用户的浏览器会因为这些Cookie不合法而忽略它们。同理,当Cookie从浏览器端传回目标服务器的时候,也会遇到相同的问题。因此有必要对Cookie进行一些处理,以保证用户的浏览器能真正识别和利用这些Cookie。
但是为何用户浏览器无法识别从目标服务器传过来的原始Cookie呢?这是因为出于安全性的考虑,Cookie规范制定的时候对Cookie的产生和接受设置了一些严格的规范,不符合这些规范的Cookie,浏览器和服务器都将予以忽略。下面我们从Cookie规范入手进行介绍。
回页首
Cookie的规范介绍
目前有以下几种Cookie规范:
Netscape cookie草案:是最早的cookie规范,基于rfc2109。尽管这个规范与rc2109有较大的差别,但是很多服务器都与之兼容。
rfc2109, 是w3c发布的第一个官方cookie规范。理论上讲,所有的服务器在处理cookie(版本1)时,都要遵循此规范。遗憾的是,这个规范太严格了,以致很多服务器不正确的实施了该规范或仍在使用Netscape规范。
rfc2965规范定义了cookie版本2,并说明了cookie版本1的不足。
rfc2965规范的使用,目前并不多。rfc2109规范相应要严格得多,在实际应用上,并不是所有的浏览器和Web服务器都严格遵守。因此相比较而言,Netscape cookie草案倒是一个比较简洁和被广泛支持的Cookie规范,因此我们在这里以Netscape cookie草案为基础进行讨论,对于其他两种规范,我们的讨论和代码具有相同的意义。关于Netscape cookie草案的细节,大家可以参照Netscape官方站点,这里我们列举一些和我们讨论有关的内容。
根据Netscape cookie草案的描述,Cookie 是Web 服务器向用户的浏览器发送的一段ASCII码文本。一旦收到Cookie,浏览器会把Cookie的信息片断以"名/值"对(name-value pairs)的形式储存保存在本地。这以后,每当向同一个Web 服务器请求一个新的文档时,Web 浏览器都会发送之站点以前存储在本地的Cookie。创建Cookie的最初目的是想让Web服务器能够通过多个HTTP请求追踪客户。有些复杂的网络应用需要在不同的网页之间保持一致,它们需要这种会话状态的保持能力。
浏览器与Web服务器通过HTTP协议进行通讯,而Cookie就是保存在HTTP协议的请求或者应答头部(在HTTP协议中,数据包括两部分,一部分是头部,由一些名值对构成,用来描述要被传输数据的一些信息。一部分是主体(body),是真正的数据(如HTML页面等))进行传送的。
在HTML文档被发送之前,Web服务器通过传送HTTP 包头中的Set-Cookie 消息把一个cookie 发送到用户的浏览器中。下面是一个遵循Netscape cookie草案的完整的Set-Cookie 头:
Set-Cookie:customer=huangxp; path=/foo; domain=.ibm.com;
expires= Wednesday, 19-OCT-05 23:12:40 GMT; [secure]
Set-Cookie的每个属性解释如下:
Customer=huangxp 一个"名称=值"对,把名称customer设置为值"huangxp",这个属性在Cookie中必须有。
path=/foo 控制哪些访问能够触发cookie 的发送。如果没有指定path,cookie 会在所有对此站点的HTTP 传送时发送。如果path=/directory,只有访问/directory 下面的网页时,cookie才被发送。在这个例子中,用户在访问目录/foo下的内容时,浏览器将发送此cookie。如果指定了path,但是path与当前访问的url不符,则此cookie将被忽略。
domain=.ibm.com 指定cookie被发送到哪台计算机上。正常情况下,cookie只被送回最初向用户发送cookie 的计算机。在这个例子中,cookie 会被发送到任何在.ibm.com域中的主机。如果domain 被设为空,domain 就被设置为和提供cookie 的Web 服务器相同。如果domain不为空,并且它的值又和提供cookie的Web服务器域名不符,这个Cookie将被忽略。
expires= Wednesday, 19-OCT-05 23:12:40 GMT 指定cookie 失效的时间。如果没有指定失效时间,这个cookie 就不会被写入计算机的硬盘上,并且只持续到这次会话结束。
secure 如果secure 这个词被作为Set-Cookie 头的一部分,那么cookie 只能通过安全通道传输(目前即SSL通道)。否则,浏览器将忽略此Cookie。
一旦浏览器接收了cookie,这个cookie和对远端Web服务器的连续请求将一起被浏览器发送。例如 前一个cookie 被存入浏览器并且浏览器试图请求 URL http://www.ibm.com/foo/index.html 时,下面的HTTP 包头就被发送到远端的Web服务器。
GET /foo/index.html HTTP/1.0
Cookie:customer=huangxp
回页首
一次典型的网络浏览过程
在了解了Cookie协议的一些基本内容之后,让我们看看一次典型的网络浏览过程中浏览器如何识别和处理Cookie:
浏览器对于Web服务器应答包头中Cookie的操作步骤:
1. 从Web服务器的应答包头中提取所有的cookie。
2. 解析这些cookie的组成部分(名称,值,路径等等)。
3. 判定主机是否允许设置这些cookie。允许的话,则把这些Cookie存储在本地。
浏览器对Web服务器请求包头中所有的Cookie进行筛选的步骤:
1. 根据请求的URL和本地存储cookie的属性,判断那些Cookie能被发送给Web服务器。
2. 对于多个cookie,判定发送的顺序。
3. 把需要发送的Cookie加入到请求HTTP包头中一起发送。
回页首
由MTS代理的网络浏览过程
以上我们了解了在一个典型的浏览器与Web服务器交互的时候,Cookie的传递过程。下面我们将看到,如果在MTS代理网络浏览的过程中,不对Cookie进行修改,上面的Cookie传递过程将无法实现。
1. 假设用户希望把 http://www.ibm.com/foo/index.html 页面翻译成法文,应该使用如下的url对MTS发出请求
:
http://www.mts.com/translate?url=http://www.ibm.com/foo/index.html&language=French
2. MTS接收用户的请求,连接远程目标服务器 http://www.ibm.com/foo/index.html。目标服务器做出应答,返回HTTP头和HTML页面内容。其中,典型的HTTP头内容如下:
HTTP/1.1 200 OK
Date: Mon, 24 Oct 2005 06:54:41 GMT
Server: IBM_HTTP_Server
Cache-Control: no-cache
Content-Length: 19885
Connection: close
Set-Cookie:customer=huangxp; path=/foo; domain=.ibm.com;
expires= Wednesday, 19-OCT-05 23:12:40 GMT
Content-Type: text/html
3. MTS不对Set-Cookie后的内容作任何处理,直接把它加到用户浏览器的应答头上发送给浏览器。
4. 浏览器将从Set-Cookie中解析出domain和path的值,分别是.ibm.com和/foo,并与请求的url:http://www.mts.com/translate?url=http://www.ibm.com/foo/index.html&language=French进行比较。请求url的domain是www.mts.com,path是/,与Set-Cookie中的属性不符,所以浏览器将忽略此Cookie。
另外,在浏览器发送Cookie的时候也会遇到同样的问题,同样如上例,如果浏览器里本来已经存储了http://www.ibm.com/foo/的Cookie,但由于用户要通过MTS访问此站点,浏览器经不会把已经存储的Cookie上转到MTS中,MTS也就无法把之传递到http://ibm.com/foo/上。
基于上面Cookie规范的介绍和例证,我们能看出,浏览器在接受某一个站点的Cookie的时候,需要检查Cookie的参数domain、path、secure,看是否与当前的站点和URL相符,如果不符的话,就会忽略。另一方面。浏览器在上传Cookie的时候,也会根据当前所访问站点的属性,上传相关的Cookie,而其他的Cookie则不予上传。
至此,我们讨论了需要修改Cookie的根本原因在于Cookie规范的限制。下面我们讨论两种解决问题的思路。
回页首
解决问题的两种思路
Cookie的存在是要解决HTTP协议本身先天的缺陷-无状态性,它为用户保存了一些需要的状态信息。因此我们解决此问题的最本质的出发点,也就是找到一种途径能为用户保存Cookie所提供用户状态信息,实际上就是Name/Value对。
思路一
第一种思路就是修改目标服务器取得的Cookie,使之符合MTS站点的属性,然后作为MTS站点的Cookie存储到用户的浏览器中去。当然,这种修改必须保留原始Cookie的所有属性值,当以后访问同一个目标服务器的时候,MTS能根据保存的属性值还原出原始Cookie,然后进行提交。
具体到属性值的保存位置,没有太多选择的余地,实际上,domain,path,secure,expires这几个属性都无法利用,只有利用name=value这一属性对。我们的做法是创造一个新的Cookie,把原始Cookie的domain,path的值与name值进行编码,用分隔符附加在Name值的后面,符值给新的Cookie。这样做也同时避免了不同目标服务器如果出现同名的Cookie,将会互相覆盖的情况(Cookie规范里面也规定了,客户端以domain,path,name作为Cookie的唯一标示)。而原始Cookie的secure和expires值,直接符给新的Cookie,新Cookie的domain和path设成缺省值,这样,新Cookie就可以被浏览器正常接受。由于浏览器接受的所有Cookie的domain和path值都一样,因此每次用户对MTS提出请求时,浏览器都会把所有与MTS站点相关的Cookie上传,因此,MTS还需要还原原始的Cookie,过滤掉与目标服务器不相干的Cookie,然后上传有用的Cookie。
这种思路的优点在于Cookie存储在客户端,可以做到长期存储,浏览器自己根据Cookie的expires值做出判断,省掉很多开发的麻烦。缺点是转换的过程相对较复杂。另外还有一个缺点,也是由于Cookie规范的限制所造成的。Cookie规范对于一个浏览器同时能够存储的Cookie数量作出了规定。
总共300 个cookie
每个Cookie 4 K 的存储容量
每一个domain 或者 server 20 个cookie。
以上是浏览器所应达到的最小存储数量,超出这个限制,浏览器应该自动按照最少最近被使用的原则删除超出得Cookie。由于用户有可能通过MTS这一个网站翻译大量的目标服务器,因此浏览器存储在MTS的domain下的cookie数量就很有可能超过20个,这时候就会导致某些Cookie被删除。一般这也不会造成太大问题,因为规范是要求浏览器删除最少最近被使用的Cookie,但我们在实际测试当中发现有些浏览器并不遵守这样的规范,而是删除最新的Cookie,这就将导致用户很大的不便。
思路二
第二种思路在于把原始的Cookie组织成dataBean,存储到用户的Session当中去。这样,在用户端只需要存储一个SessionID的Cookie,而不需要存储所有目标服务器的每一个Cookie。另外,当接收到用户的又一次翻译请求时,再从Session当中取出所有的dataBean,逐一进行分析,找出与用户所请求的目标服务器相符的原始Cookie,进行提交。
这种思路可以克服上一种思路中Cookie超过标准数量时的缺陷,而且不需编码保存原始的Cookie属性值,减少了程序的复杂度。缺点是需要程序员自己处理expires。而且由于是把Cookie存储在Session中,一旦Session失效,所有Cookie都将被删除,所以,无法保存那些长期的Cookie。
总之,两种思路各有利弊,在实际应用当中要权衡考虑。下面我们针对两种思路进行技术实现,分别对应方案一和方案二。
由于MTS需要与目标服务器连接,遵循HTTP协议读取和返回Cookie,但是如果用JDK中的java.net.URLConnection处理Cookie将非常不方便,因此我们使用HTTPClient来处理与目标服务器的连接。
回页首
方案一:Cookie存储在浏览器端
用户每发起一次新的请求,浏览器在检查完本地存储Cookie的有效性后,会把所有由MTS产生的有效Cookie附加在请求头里送到MTS。MTS接受到客户端的翻译请求后,从Request中提取出所有的Cookie,还原后根据目标服务器的domain和path进行过滤。产生所有与目标服务器相关的Cookie。
//从request中获取所有的Cookie
javax.servlet.http.Cookie[] theCookies = request.getCookies();
ArrayList cookiesList = new ArrayList();
String url = request.getParameter("url");
String domain = URLUtil.getURLHost(url);
String path = URLUtil.getPath(url);
if (theCookies != null)
{
for (int i = 0; i < theCookies.length; i++)
{
RE r = new RE();
//用正则表达式把name项还原成domain,path,name
REDebugCompiler compiler = new REDebugCompiler();
r.setProgram(compiler.compile("\\|\\|"));
String[] values = r.split(theCookies[i].getName());
//"9.181.116.183||/MTModule||testCookie:value1" or " ||
||testCookie:value1"
if (values.length == 3)
{
if (values[0].trim().startsWith("."))
{
if (!domain.endsWith(values[0].trim()))
continue;
} else if (!domain.endsWith("://" + values[0].trim()))
continue;
if (!path.startsWith(values[1].trim()))
continue;
Cookie tempCookie = new Cookie();
tempCookie.setDomain(
("".equals(values[0].trim())) ? null : values[0]);
tempCookie.setPath(
("".equals(values[1].trim())) ? null : values[1]);
tempCookie.setName(
("".equals(values[2].trim())) ? null : values[2]);
tempCookie.setSecure(theCookies[i].getSecure());
tempCookie.setValue(theCookies[i].getValue());
tempCookie.setVersion(theCookies[i].getVersion());
tempCookie.setComment(theCookies[i].getComment());
cookiesList.add(tempCookie);
}
}
}
//transferedCookie用来存储将被传到目标服务器的Cookie
Cookie[] transferedCookie = new Cookie[cookiesList.size()];
cookiesList.toArray(transferedCookie);
接下来,需要把Cookie送到目标服务器中。我们使用HTTPClient与目标服务器连接。HTTPClient在与目标服务器连接以后,允许服务器设置Cookie并在需要的时候自动将Cookie返回服务器,也支持手工设置 Cookie后发送到服务器端。但是,由于如何处理cookie有几个规范互相冲突:Netscape Cookie 草案、RFC2109、RFC2965,而且还有很大数量的软件商的Cookie实现不遵循任何规范。 为了处理这种状况,需要把HttpClient设置成Cookie兼容模式,这样可以最大限度的处理好各种Cookie。下面的代码把Cookie送到目标服务器。
HttpClient client = new HttpClient();
//从request得到所有需要传输的cookie
Cookie[] questCookie = getCookieFromRequest(request);
//设置HTTPClient为Cookie兼容模式
client.getState().setCookiePolicy(CookiePolicy.COMPATIBILITY);
if (questCookie.length > 0)
//把Cookie加到httpclient中
client.getState().addCookies(questCookie);
HttpMethod method = new GetMethod(TagerURL);
//向目标服务器发送请求
int statusCode = client.executeMethod(method);
method.releaseConnection();
MTS把请求和Cookie送出后,继续接收目标服务器的应答,读取返回的原始Cookie,并转换成可以存储在用户浏览器端的Cookie。下面的代码将对原始Cookie的内容进行变换,保留expires和secure等项,把domain和path项编码到name中去。
//从HTTPClient中取得所有的Cookie
Cookie[] temp = client.getState().getCookies();
if (temp != null)
{
javax.servlet.httpCookie theCookie = new javax.servlet.http.Cookie[temp.length];
//逐一对Cookie进行处理
for (int i = 0; i < temp.length; i++)
{ StringBuffer sb = new StringBuffer();
//编码成domain||path||name
sb.append(
temp[i].getDomain() == null ? " " : temp[i].getDomain());
sb.append("||");
sb.append(temp[i].getPath() == null ? " " : temp[i].getPath());
sb.append("||");
sb.append(temp[i].getName() == null ? " " : temp[i].getName());
theCookie[i] =
new Cookie(sb.toString(),temp[i].getValue());
//复制其他项
theCookie[i].setMaxAge(theCookie[i].getMaxAge();
theCookie[i].setSecure(temp[i].getSecure());
theCookie[i].setVersion(temp[i].getVersion());
theCookie[i].setComment(temp[i].getComment());
}
}
最后一步,把这些Cookie保存到response里,随HTTP应答头返回用户浏览器。并保存在浏览器中。
//把所有转换后的Cookie加入response
for (int i = 0; i < theCookie.length; i++) {
response.addCookie(theCookie[i]);
}
至此,我们已经完成了接收用户请求,转换Cookie,发送到目标服务器,接收目标服务器的原始Cookie,并保存在客户浏览器的整个处理过程。
回页首
方案二:Cookie存储在服务器端
在此种方案中,目标服务器返回给MTS的Cookie将被组织成dataBean,存储在用户的Session中。因此,我们首先生成一个用来存储Cookie的类CookiesBean,根据它的特性,它可以继承ArraryList类。此对象将存储用户访问目标服务器时接收到的所有Cookie,并提供与新接收到的Cookie融合的功能,同时能够删除过期的Cookie,更新同名的Cookie。
public class CookiesBean extends ArrayList
{
/**
* 处理Cookies.
* @参数 Cookies array
*/
public CookiesBean(Cookie[] cook)
{
if (cook == null)
return;
//add all cookie which isn't expired.
for (int i = 0; i < cook.length; i++)
{
if (!cook[i].isExpired())
{
add(cook[i]);
}
}
}
/**
* 融合参数中的bean
* @参数 bean
* 参考: rfc2109 4.3.3 Cookie Management
*/
public void RefreshBean(CookiesBean bean)
{
if (bean == null)
return;
Iterator it = bean.iterator();
//针对bean中的每一个Cookie进行处理
while (it.hasNext())
{
Cookie beanCookie = (Cookie) it.next();
if (beanCookie == null) continue;
ArrayList drop = new ArrayList();
Iterator thisIt = iterator();
//取出存储的Cookie进行比较和处理
while (thisIt.hasNext())
{
Cookie thisCookie = (Cookie) thisIt.next();
if (thisCookie == null) continue;
//比较name,domain和path,如果一样的话,则把此Cookie移到drop中
if (CommonMethods
.CompString(beanCookie.getName(), thisCookie.getName())
&& CommonMethods.CompString(
beanCookie.getDomain(),
thisCookie.getDomain())
&& CommonMethods.CompString(
beanCookie.getPath(),
thisCookie.getPath()))
{
drop.add(thisCookie);
continue;
}
//删除过期的Cookie
if (thisCookie.isExpired())
drop.add(thisCookie);
}
//删除所有drop中的Cookie
this.removeAll(drop);
//如果beanCookie有效,则加入到存储区中。
if (!beanCookie.isExpired())
add(beanCookie);
}
return;
}
}
当MTS接受到客户端的翻译请求后,会从Session中提取出所有的dataBean,并得到存储的所有Cookie。如以下代码:
CookiesBean dataBean = null;
Cookie[] theCookies = new Cookie[0];
ArrayList cookiesList = new ArrayList();
//获得Session,并获得dataBean
HttpSession session = request.getSession(false);
if (session != null)
{
dataBean = (CookiesBean) session.getAttribute(SESSION_NAME);
}
else
{
return theCookies;
}
MTS在所有的存储的Cookie中,检查Cookie的Domain、path和secure的值,筛选出符合目标服务器的Cookie。
//提取目标服务器的domain和path
String url = context.getURL();
String domain = URLUtil.getURLHost(url);
String path = url.substring(domain.length());
String cookiedomain = null;
String cookiepath = null;
//逐个比较Cookie的domain和path
//把符合要求的Cookie纪录到cookiesList中
for (int i = 0; i < dataBean.size(); i++)
{
Cookie cookie = (Cookie) dataBean.get(i);
if (cookie == null) continue;
cookiedomain =
(cookie.getDomain() == null) ? "" : cookie.getDomain();
cookiepath = (cookie.getPath() == null) ? " " : cookie.getPath();
if (!path.startsWith(cookiepath))
continue;
if (cookiedomain.startsWith("."))
{
if (!domain.endsWith(cookiedomain))
continue;
}
else if (!domain.endsWith("://" + cookiedomain))
continue;
if (cookie.isExpired())
continue;
if (cookie.getSecure() && url.toLowerCase().startsWith("http:"))
continue;
cookiesList.add(cookie);
}
theCookies = new Cookie[cookiesList.size()];
cookiesList.toArray(theCookies);
return theCookies;
把Cookie送到目标服务器的代码与方案一基本一样,在此忽略。
最后一步,需要把Cookie存储到Session中。下面的代码将从目标服务器接受Cookie,融入到dataBean中,并保存到客户的Session中。
//从目标服务器得到Cookie集
Cookie[] cookies = client.getState().getCookies();
CookiesBean bean = new CookiesBean(cookies);
CookiesBean dataBean = bean;
//取得用户Session
HttpSession session = request.getSession(false);
if (session != null)
{
if (session.getAttribute(SESSION_NAME) != null)
{
//读取Session中存取的dataBean
dataBean = (CookiesBean) session.getAttribute(SESSION_NAME);
//目标服务器端的Cookie融合到Session中的dataBean中
dataBean.RefreshBean(bean);
}
//把最终的dataBean存入Session中
session.setAttribute(SESSION_NAME, dataBean);
}
至此,我们已经完成了在Session中保存个目标服务器所产生Cookie的整个处理过程。
回页首
关于Session的考虑
在研究完如何管理和传递Cookie之后,我们也需要研究一下Session的传递。因为目前大部分站点都在采用Session机制保存用户状态数据,如果不能解决Session的传递问题,HTTP应用代理服务器的适用范围同样会大打折扣。
首先我们了解一下Session的实现机制。Session是一种服务器端的机制,服务器使用一种类似于散列表的结构来保存信息。当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个),session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串。
保存这个session id的方式之一就是采用Cookie。一般这个Cookie的名字都类似于 SESSIONID。比如WebSphere对于Web应用程序生成的Cookie:JSESSIONID= 0001HWF4iVD94pY8Cpbx6U4CXkf:10lro0398,它的名字就是 JSESSIONID。
保存session id的其他方式还包括URL重写和表单隐藏字段。这两种方式都不需要代理服务器作特殊处理。因此实际上,我们解决了Cookie的管理和传递的问题之后,也就解决了Session的管理和传递。
http://www.ibm.com/developerworks/cn/java/j-cookie/
读者定位为具有Java和Web开发经验的开发和设计人员。
读者可以学习到关于Cookie的工作原理和Cookie协议的细节,以及在一个HTTP应用代理的场景下Cookie的管理和处理思想,并可以直接使用文中的代码和思路,提高工作效率。
随着越来越多的系统移植到了Web上,HTTP协议具有了比以前更广泛的应用。不同的系统对WEB实现提出了不同的要求,基于HTTP协议的网络应用正趋于复杂化和多元化。很多应用需要把用户请求的页面进行处理后再返回给用户,比如页面关键字过滤,页面内容缓存、内容搜索、页面翻译等等。这些应用在实际效果上类似于一个HTTP应用代理:它们首先接受用户的请求,根据用户请求的URL去真正的目标服务器取回目标页面,再根据不同应用的要求做出相应处理后返回给用户。这样用户直接面对的就是这个HTTP应用代理,而通过它与其他页面进行交互。Cookie或Session技术的应用,解决了HTTP协议的一个问题 -- 无法保持客户状态,因此它现在被广泛应用于各种Web站点中。上面提到的那些应用如果不能处理好Cookie和Session的传递、更新和废除等问题,就会极大的限制它们所能处理站点的范围,因此如何在HTTP应用代理中正确处理Cookie,成为一个必须解决的问题。本文结合在页面翻译(Machine Translation System)项目中对于Cookie的处理方法,探讨一下这方面的解决方案。
MTS项目简介及讨论前提
Machine Translation System(以下简称MTS)是一个在线实时页面翻译系统,为用户在线提供把英文页面翻译成其他9种语言的服务。用户通过向MTS系统提交一个类似下面的URL使用此服务,其中参数url指明了用户所需要翻译的目标地址,参数language指明了所需翻译成的目标语言,www.mts.com是假想中提供MTS服务的站点。
HTTP://www.mts.com/translate?url=http://www.ibm.com/&language=French
一个完整的MTS系统处理过程可以分解成以下几个步骤:
用户向MTS提交合适的URL。
MTS在接到用户的请求后,解析出用户需要翻译的目标地址和目标语言,根据用户请求的目标地址,把请求转发到目标服务器。
MTS接受来自目标服务器的应答,包括页面信息和HTTP头信息。
MTS在确定得到正确的目标页面后,把页面内容送入WebSphere Translation Server进行翻译。
把翻译后的页面连同修改后的HTTP头信息提交给用户。
MTS逻辑图
当然,这其中涉及到很多的应用处理。比如与各种HTTP/HTTPS站点建立联结、根据HTTP头信息进行页面跳转和错误处理、为始终保持用户在翻译模式下而对目标的HTML页面进行分析和修改,根据系统设置对某些DNT(Do Not Translate)的页面进行过滤和跳转,当然还有对Cookie的处理等等。其他问题跟这篇文章关联不大,我们重点讨论在这种情况下的Cookie处理。Cookie跟随目标服务器的HTTP头信息被MTS接收到,经过MTS整理之后发给客户端浏览器。MTS在接到下一次用户对同一个站点的翻译请求时,再把从客户端得到的Cookie发送给目标服务器。
在以上的场景中,MTS充当的作用类似于一种HTTP应用代理服务器,它代替用户取得目标页面,并在作出相应处理后再提交给用户。当然,这种代理服务器不需要用户修改浏览器的代理服务器参数或者网络配置,而只是简单的在浏览器的地址栏中输入一个MTS能够识别的URL即可。此篇文章也是在这样一个应用场景的基础上,展开对HTTP应用代理服务器如何处理Cookie的讨论。
回页首
问题的产生
在MTS系统中,目标服务器的Cookie在两个地方会产生问题。当MTS接收目标服务器应答的时候,Cookie随着HTTP头信息被MTS接收到的。这时候目标服务器认为MTS就是最终客户,因此它赋予了Cookie与目标服务器相符的属性。而如果MTS把这些Cookie原封不动的保存在HTTP头信息中,传给真正的最终用户的话,用户的浏览器会因为这些Cookie不合法而忽略它们。同理,当Cookie从浏览器端传回目标服务器的时候,也会遇到相同的问题。因此有必要对Cookie进行一些处理,以保证用户的浏览器能真正识别和利用这些Cookie。
但是为何用户浏览器无法识别从目标服务器传过来的原始Cookie呢?这是因为出于安全性的考虑,Cookie规范制定的时候对Cookie的产生和接受设置了一些严格的规范,不符合这些规范的Cookie,浏览器和服务器都将予以忽略。下面我们从Cookie规范入手进行介绍。
回页首
Cookie的规范介绍
目前有以下几种Cookie规范:
Netscape cookie草案:是最早的cookie规范,基于rfc2109。尽管这个规范与rc2109有较大的差别,但是很多服务器都与之兼容。
rfc2109, 是w3c发布的第一个官方cookie规范。理论上讲,所有的服务器在处理cookie(版本1)时,都要遵循此规范。遗憾的是,这个规范太严格了,以致很多服务器不正确的实施了该规范或仍在使用Netscape规范。
rfc2965规范定义了cookie版本2,并说明了cookie版本1的不足。
rfc2965规范的使用,目前并不多。rfc2109规范相应要严格得多,在实际应用上,并不是所有的浏览器和Web服务器都严格遵守。因此相比较而言,Netscape cookie草案倒是一个比较简洁和被广泛支持的Cookie规范,因此我们在这里以Netscape cookie草案为基础进行讨论,对于其他两种规范,我们的讨论和代码具有相同的意义。关于Netscape cookie草案的细节,大家可以参照Netscape官方站点,这里我们列举一些和我们讨论有关的内容。
根据Netscape cookie草案的描述,Cookie 是Web 服务器向用户的浏览器发送的一段ASCII码文本。一旦收到Cookie,浏览器会把Cookie的信息片断以"名/值"对(name-value pairs)的形式储存保存在本地。这以后,每当向同一个Web 服务器请求一个新的文档时,Web 浏览器都会发送之站点以前存储在本地的Cookie。创建Cookie的最初目的是想让Web服务器能够通过多个HTTP请求追踪客户。有些复杂的网络应用需要在不同的网页之间保持一致,它们需要这种会话状态的保持能力。
浏览器与Web服务器通过HTTP协议进行通讯,而Cookie就是保存在HTTP协议的请求或者应答头部(在HTTP协议中,数据包括两部分,一部分是头部,由一些名值对构成,用来描述要被传输数据的一些信息。一部分是主体(body),是真正的数据(如HTML页面等))进行传送的。
在HTML文档被发送之前,Web服务器通过传送HTTP 包头中的Set-Cookie 消息把一个cookie 发送到用户的浏览器中。下面是一个遵循Netscape cookie草案的完整的Set-Cookie 头:
Set-Cookie:customer=huangxp; path=/foo; domain=.ibm.com;
expires= Wednesday, 19-OCT-05 23:12:40 GMT; [secure]
Set-Cookie的每个属性解释如下:
Customer=huangxp 一个"名称=值"对,把名称customer设置为值"huangxp",这个属性在Cookie中必须有。
path=/foo 控制哪些访问能够触发cookie 的发送。如果没有指定path,cookie 会在所有对此站点的HTTP 传送时发送。如果path=/directory,只有访问/directory 下面的网页时,cookie才被发送。在这个例子中,用户在访问目录/foo下的内容时,浏览器将发送此cookie。如果指定了path,但是path与当前访问的url不符,则此cookie将被忽略。
domain=.ibm.com 指定cookie被发送到哪台计算机上。正常情况下,cookie只被送回最初向用户发送cookie 的计算机。在这个例子中,cookie 会被发送到任何在.ibm.com域中的主机。如果domain 被设为空,domain 就被设置为和提供cookie 的Web 服务器相同。如果domain不为空,并且它的值又和提供cookie的Web服务器域名不符,这个Cookie将被忽略。
expires= Wednesday, 19-OCT-05 23:12:40 GMT 指定cookie 失效的时间。如果没有指定失效时间,这个cookie 就不会被写入计算机的硬盘上,并且只持续到这次会话结束。
secure 如果secure 这个词被作为Set-Cookie 头的一部分,那么cookie 只能通过安全通道传输(目前即SSL通道)。否则,浏览器将忽略此Cookie。
一旦浏览器接收了cookie,这个cookie和对远端Web服务器的连续请求将一起被浏览器发送。例如 前一个cookie 被存入浏览器并且浏览器试图请求 URL http://www.ibm.com/foo/index.html 时,下面的HTTP 包头就被发送到远端的Web服务器。
GET /foo/index.html HTTP/1.0
Cookie:customer=huangxp
回页首
一次典型的网络浏览过程
在了解了Cookie协议的一些基本内容之后,让我们看看一次典型的网络浏览过程中浏览器如何识别和处理Cookie:
浏览器对于Web服务器应答包头中Cookie的操作步骤:
1. 从Web服务器的应答包头中提取所有的cookie。
2. 解析这些cookie的组成部分(名称,值,路径等等)。
3. 判定主机是否允许设置这些cookie。允许的话,则把这些Cookie存储在本地。
浏览器对Web服务器请求包头中所有的Cookie进行筛选的步骤:
1. 根据请求的URL和本地存储cookie的属性,判断那些Cookie能被发送给Web服务器。
2. 对于多个cookie,判定发送的顺序。
3. 把需要发送的Cookie加入到请求HTTP包头中一起发送。
回页首
由MTS代理的网络浏览过程
以上我们了解了在一个典型的浏览器与Web服务器交互的时候,Cookie的传递过程。下面我们将看到,如果在MTS代理网络浏览的过程中,不对Cookie进行修改,上面的Cookie传递过程将无法实现。
1. 假设用户希望把 http://www.ibm.com/foo/index.html 页面翻译成法文,应该使用如下的url对MTS发出请求
:
http://www.mts.com/translate?url=http://www.ibm.com/foo/index.html&language=French
2. MTS接收用户的请求,连接远程目标服务器 http://www.ibm.com/foo/index.html。目标服务器做出应答,返回HTTP头和HTML页面内容。其中,典型的HTTP头内容如下:
HTTP/1.1 200 OK
Date: Mon, 24 Oct 2005 06:54:41 GMT
Server: IBM_HTTP_Server
Cache-Control: no-cache
Content-Length: 19885
Connection: close
Set-Cookie:customer=huangxp; path=/foo; domain=.ibm.com;
expires= Wednesday, 19-OCT-05 23:12:40 GMT
Content-Type: text/html
3. MTS不对Set-Cookie后的内容作任何处理,直接把它加到用户浏览器的应答头上发送给浏览器。
4. 浏览器将从Set-Cookie中解析出domain和path的值,分别是.ibm.com和/foo,并与请求的url:http://www.mts.com/translate?url=http://www.ibm.com/foo/index.html&language=French进行比较。请求url的domain是www.mts.com,path是/,与Set-Cookie中的属性不符,所以浏览器将忽略此Cookie。
另外,在浏览器发送Cookie的时候也会遇到同样的问题,同样如上例,如果浏览器里本来已经存储了http://www.ibm.com/foo/的Cookie,但由于用户要通过MTS访问此站点,浏览器经不会把已经存储的Cookie上转到MTS中,MTS也就无法把之传递到http://ibm.com/foo/上。
基于上面Cookie规范的介绍和例证,我们能看出,浏览器在接受某一个站点的Cookie的时候,需要检查Cookie的参数domain、path、secure,看是否与当前的站点和URL相符,如果不符的话,就会忽略。另一方面。浏览器在上传Cookie的时候,也会根据当前所访问站点的属性,上传相关的Cookie,而其他的Cookie则不予上传。
至此,我们讨论了需要修改Cookie的根本原因在于Cookie规范的限制。下面我们讨论两种解决问题的思路。
回页首
解决问题的两种思路
Cookie的存在是要解决HTTP协议本身先天的缺陷-无状态性,它为用户保存了一些需要的状态信息。因此我们解决此问题的最本质的出发点,也就是找到一种途径能为用户保存Cookie所提供用户状态信息,实际上就是Name/Value对。
思路一
第一种思路就是修改目标服务器取得的Cookie,使之符合MTS站点的属性,然后作为MTS站点的Cookie存储到用户的浏览器中去。当然,这种修改必须保留原始Cookie的所有属性值,当以后访问同一个目标服务器的时候,MTS能根据保存的属性值还原出原始Cookie,然后进行提交。
具体到属性值的保存位置,没有太多选择的余地,实际上,domain,path,secure,expires这几个属性都无法利用,只有利用name=value这一属性对。我们的做法是创造一个新的Cookie,把原始Cookie的domain,path的值与name值进行编码,用分隔符附加在Name值的后面,符值给新的Cookie。这样做也同时避免了不同目标服务器如果出现同名的Cookie,将会互相覆盖的情况(Cookie规范里面也规定了,客户端以domain,path,name作为Cookie的唯一标示)。而原始Cookie的secure和expires值,直接符给新的Cookie,新Cookie的domain和path设成缺省值,这样,新Cookie就可以被浏览器正常接受。由于浏览器接受的所有Cookie的domain和path值都一样,因此每次用户对MTS提出请求时,浏览器都会把所有与MTS站点相关的Cookie上传,因此,MTS还需要还原原始的Cookie,过滤掉与目标服务器不相干的Cookie,然后上传有用的Cookie。
这种思路的优点在于Cookie存储在客户端,可以做到长期存储,浏览器自己根据Cookie的expires值做出判断,省掉很多开发的麻烦。缺点是转换的过程相对较复杂。另外还有一个缺点,也是由于Cookie规范的限制所造成的。Cookie规范对于一个浏览器同时能够存储的Cookie数量作出了规定。
总共300 个cookie
每个Cookie 4 K 的存储容量
每一个domain 或者 server 20 个cookie。
以上是浏览器所应达到的最小存储数量,超出这个限制,浏览器应该自动按照最少最近被使用的原则删除超出得Cookie。由于用户有可能通过MTS这一个网站翻译大量的目标服务器,因此浏览器存储在MTS的domain下的cookie数量就很有可能超过20个,这时候就会导致某些Cookie被删除。一般这也不会造成太大问题,因为规范是要求浏览器删除最少最近被使用的Cookie,但我们在实际测试当中发现有些浏览器并不遵守这样的规范,而是删除最新的Cookie,这就将导致用户很大的不便。
思路二
第二种思路在于把原始的Cookie组织成dataBean,存储到用户的Session当中去。这样,在用户端只需要存储一个SessionID的Cookie,而不需要存储所有目标服务器的每一个Cookie。另外,当接收到用户的又一次翻译请求时,再从Session当中取出所有的dataBean,逐一进行分析,找出与用户所请求的目标服务器相符的原始Cookie,进行提交。
这种思路可以克服上一种思路中Cookie超过标准数量时的缺陷,而且不需编码保存原始的Cookie属性值,减少了程序的复杂度。缺点是需要程序员自己处理expires。而且由于是把Cookie存储在Session中,一旦Session失效,所有Cookie都将被删除,所以,无法保存那些长期的Cookie。
总之,两种思路各有利弊,在实际应用当中要权衡考虑。下面我们针对两种思路进行技术实现,分别对应方案一和方案二。
由于MTS需要与目标服务器连接,遵循HTTP协议读取和返回Cookie,但是如果用JDK中的java.net.URLConnection处理Cookie将非常不方便,因此我们使用HTTPClient来处理与目标服务器的连接。
回页首
方案一:Cookie存储在浏览器端
用户每发起一次新的请求,浏览器在检查完本地存储Cookie的有效性后,会把所有由MTS产生的有效Cookie附加在请求头里送到MTS。MTS接受到客户端的翻译请求后,从Request中提取出所有的Cookie,还原后根据目标服务器的domain和path进行过滤。产生所有与目标服务器相关的Cookie。
//从request中获取所有的Cookie
javax.servlet.http.Cookie[] theCookies = request.getCookies();
ArrayList cookiesList = new ArrayList();
String url = request.getParameter("url");
String domain = URLUtil.getURLHost(url);
String path = URLUtil.getPath(url);
if (theCookies != null)
{
for (int i = 0; i < theCookies.length; i++)
{
RE r = new RE();
//用正则表达式把name项还原成domain,path,name
REDebugCompiler compiler = new REDebugCompiler();
r.setProgram(compiler.compile("\\|\\|"));
String[] values = r.split(theCookies[i].getName());
//"9.181.116.183||/MTModule||testCookie:value1" or " ||
||testCookie:value1"
if (values.length == 3)
{
if (values[0].trim().startsWith("."))
{
if (!domain.endsWith(values[0].trim()))
continue;
} else if (!domain.endsWith("://" + values[0].trim()))
continue;
if (!path.startsWith(values[1].trim()))
continue;
Cookie tempCookie = new Cookie();
tempCookie.setDomain(
("".equals(values[0].trim())) ? null : values[0]);
tempCookie.setPath(
("".equals(values[1].trim())) ? null : values[1]);
tempCookie.setName(
("".equals(values[2].trim())) ? null : values[2]);
tempCookie.setSecure(theCookies[i].getSecure());
tempCookie.setValue(theCookies[i].getValue());
tempCookie.setVersion(theCookies[i].getVersion());
tempCookie.setComment(theCookies[i].getComment());
cookiesList.add(tempCookie);
}
}
}
//transferedCookie用来存储将被传到目标服务器的Cookie
Cookie[] transferedCookie = new Cookie[cookiesList.size()];
cookiesList.toArray(transferedCookie);
接下来,需要把Cookie送到目标服务器中。我们使用HTTPClient与目标服务器连接。HTTPClient在与目标服务器连接以后,允许服务器设置Cookie并在需要的时候自动将Cookie返回服务器,也支持手工设置 Cookie后发送到服务器端。但是,由于如何处理cookie有几个规范互相冲突:Netscape Cookie 草案、RFC2109、RFC2965,而且还有很大数量的软件商的Cookie实现不遵循任何规范。 为了处理这种状况,需要把HttpClient设置成Cookie兼容模式,这样可以最大限度的处理好各种Cookie。下面的代码把Cookie送到目标服务器。
HttpClient client = new HttpClient();
//从request得到所有需要传输的cookie
Cookie[] questCookie = getCookieFromRequest(request);
//设置HTTPClient为Cookie兼容模式
client.getState().setCookiePolicy(CookiePolicy.COMPATIBILITY);
if (questCookie.length > 0)
//把Cookie加到httpclient中
client.getState().addCookies(questCookie);
HttpMethod method = new GetMethod(TagerURL);
//向目标服务器发送请求
int statusCode = client.executeMethod(method);
method.releaseConnection();
MTS把请求和Cookie送出后,继续接收目标服务器的应答,读取返回的原始Cookie,并转换成可以存储在用户浏览器端的Cookie。下面的代码将对原始Cookie的内容进行变换,保留expires和secure等项,把domain和path项编码到name中去。
//从HTTPClient中取得所有的Cookie
Cookie[] temp = client.getState().getCookies();
if (temp != null)
{
javax.servlet.httpCookie theCookie = new javax.servlet.http.Cookie[temp.length];
//逐一对Cookie进行处理
for (int i = 0; i < temp.length; i++)
{ StringBuffer sb = new StringBuffer();
//编码成domain||path||name
sb.append(
temp[i].getDomain() == null ? " " : temp[i].getDomain());
sb.append("||");
sb.append(temp[i].getPath() == null ? " " : temp[i].getPath());
sb.append("||");
sb.append(temp[i].getName() == null ? " " : temp[i].getName());
theCookie[i] =
new Cookie(sb.toString(),temp[i].getValue());
//复制其他项
theCookie[i].setMaxAge(theCookie[i].getMaxAge();
theCookie[i].setSecure(temp[i].getSecure());
theCookie[i].setVersion(temp[i].getVersion());
theCookie[i].setComment(temp[i].getComment());
}
}
最后一步,把这些Cookie保存到response里,随HTTP应答头返回用户浏览器。并保存在浏览器中。
//把所有转换后的Cookie加入response
for (int i = 0; i < theCookie.length; i++) {
response.addCookie(theCookie[i]);
}
至此,我们已经完成了接收用户请求,转换Cookie,发送到目标服务器,接收目标服务器的原始Cookie,并保存在客户浏览器的整个处理过程。
回页首
方案二:Cookie存储在服务器端
在此种方案中,目标服务器返回给MTS的Cookie将被组织成dataBean,存储在用户的Session中。因此,我们首先生成一个用来存储Cookie的类CookiesBean,根据它的特性,它可以继承ArraryList类。此对象将存储用户访问目标服务器时接收到的所有Cookie,并提供与新接收到的Cookie融合的功能,同时能够删除过期的Cookie,更新同名的Cookie。
public class CookiesBean extends ArrayList
{
/**
* 处理Cookies.
* @参数 Cookies array
*/
public CookiesBean(Cookie[] cook)
{
if (cook == null)
return;
//add all cookie which isn't expired.
for (int i = 0; i < cook.length; i++)
{
if (!cook[i].isExpired())
{
add(cook[i]);
}
}
}
/**
* 融合参数中的bean
* @参数 bean
* 参考: rfc2109 4.3.3 Cookie Management
*/
public void RefreshBean(CookiesBean bean)
{
if (bean == null)
return;
Iterator it = bean.iterator();
//针对bean中的每一个Cookie进行处理
while (it.hasNext())
{
Cookie beanCookie = (Cookie) it.next();
if (beanCookie == null) continue;
ArrayList drop = new ArrayList();
Iterator thisIt = iterator();
//取出存储的Cookie进行比较和处理
while (thisIt.hasNext())
{
Cookie thisCookie = (Cookie) thisIt.next();
if (thisCookie == null) continue;
//比较name,domain和path,如果一样的话,则把此Cookie移到drop中
if (CommonMethods
.CompString(beanCookie.getName(), thisCookie.getName())
&& CommonMethods.CompString(
beanCookie.getDomain(),
thisCookie.getDomain())
&& CommonMethods.CompString(
beanCookie.getPath(),
thisCookie.getPath()))
{
drop.add(thisCookie);
continue;
}
//删除过期的Cookie
if (thisCookie.isExpired())
drop.add(thisCookie);
}
//删除所有drop中的Cookie
this.removeAll(drop);
//如果beanCookie有效,则加入到存储区中。
if (!beanCookie.isExpired())
add(beanCookie);
}
return;
}
}
当MTS接受到客户端的翻译请求后,会从Session中提取出所有的dataBean,并得到存储的所有Cookie。如以下代码:
CookiesBean dataBean = null;
Cookie[] theCookies = new Cookie[0];
ArrayList cookiesList = new ArrayList();
//获得Session,并获得dataBean
HttpSession session = request.getSession(false);
if (session != null)
{
dataBean = (CookiesBean) session.getAttribute(SESSION_NAME);
}
else
{
return theCookies;
}
MTS在所有的存储的Cookie中,检查Cookie的Domain、path和secure的值,筛选出符合目标服务器的Cookie。
//提取目标服务器的domain和path
String url = context.getURL();
String domain = URLUtil.getURLHost(url);
String path = url.substring(domain.length());
String cookiedomain = null;
String cookiepath = null;
//逐个比较Cookie的domain和path
//把符合要求的Cookie纪录到cookiesList中
for (int i = 0; i < dataBean.size(); i++)
{
Cookie cookie = (Cookie) dataBean.get(i);
if (cookie == null) continue;
cookiedomain =
(cookie.getDomain() == null) ? "" : cookie.getDomain();
cookiepath = (cookie.getPath() == null) ? " " : cookie.getPath();
if (!path.startsWith(cookiepath))
continue;
if (cookiedomain.startsWith("."))
{
if (!domain.endsWith(cookiedomain))
continue;
}
else if (!domain.endsWith("://" + cookiedomain))
continue;
if (cookie.isExpired())
continue;
if (cookie.getSecure() && url.toLowerCase().startsWith("http:"))
continue;
cookiesList.add(cookie);
}
theCookies = new Cookie[cookiesList.size()];
cookiesList.toArray(theCookies);
return theCookies;
把Cookie送到目标服务器的代码与方案一基本一样,在此忽略。
最后一步,需要把Cookie存储到Session中。下面的代码将从目标服务器接受Cookie,融入到dataBean中,并保存到客户的Session中。
//从目标服务器得到Cookie集
Cookie[] cookies = client.getState().getCookies();
CookiesBean bean = new CookiesBean(cookies);
CookiesBean dataBean = bean;
//取得用户Session
HttpSession session = request.getSession(false);
if (session != null)
{
if (session.getAttribute(SESSION_NAME) != null)
{
//读取Session中存取的dataBean
dataBean = (CookiesBean) session.getAttribute(SESSION_NAME);
//目标服务器端的Cookie融合到Session中的dataBean中
dataBean.RefreshBean(bean);
}
//把最终的dataBean存入Session中
session.setAttribute(SESSION_NAME, dataBean);
}
至此,我们已经完成了在Session中保存个目标服务器所产生Cookie的整个处理过程。
回页首
关于Session的考虑
在研究完如何管理和传递Cookie之后,我们也需要研究一下Session的传递。因为目前大部分站点都在采用Session机制保存用户状态数据,如果不能解决Session的传递问题,HTTP应用代理服务器的适用范围同样会大打折扣。
首先我们了解一下Session的实现机制。Session是一种服务器端的机制,服务器使用一种类似于散列表的结构来保存信息。当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个),session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串。
保存这个session id的方式之一就是采用Cookie。一般这个Cookie的名字都类似于 SESSIONID。比如WebSphere对于Web应用程序生成的Cookie:JSESSIONID= 0001HWF4iVD94pY8Cpbx6U4CXkf:10lro0398,它的名字就是 JSESSIONID。
保存session id的其他方式还包括URL重写和表单隐藏字段。这两种方式都不需要代理服务器作特殊处理。因此实际上,我们解决了Cookie的管理和传递的问题之后,也就解决了Session的管理和传递。
http://www.ibm.com/developerworks/cn/java/j-cookie/
发表评论
-
java泛型中的上界下界(上限下限) (转)
2013-01-23 18:02 1095java泛型中的super关键字不太常用,也不太好理解,今天又 ... -
Ant中过滤器的使用
2011-12-07 20:06 1002Ant中过滤器的使用 <?xml version=&q ... -
将实体声明和实体引用读入 DOM
2011-06-17 17:10 1295实体是一个声明,指定 ... -
多线程编程 实战篇 (四)
2010-12-07 17:48 1031[线程的中断] 不客气地 ... -
Java Thread.interrupt 害人! 中断JAVA线程(zz)
2010-12-07 17:42 3747程序是很简易的。然而,在编程人员面前,多线程呈现出了一组新的难 ... -
Flex+%2B+LCDS+%2B+Java+入门教程
2010-09-03 16:07 862Flex+LCDS+Java+入门教程 -
Flex完全自学入门手册
2010-09-03 13:08 891http://lichen.blog.51cto.com/69 ... -
java 5 多线程sun教程
2010-05-21 10:43 1199http://gceclub.sun.com.cn/Concu ... -
ibm的nio教程
2010-04-12 18:13 1148ibm的nio教程 -
cas单点登陆
2009-12-17 17:05 839http://blog.csdn.net/DL88250/ar ... -
CAS项目实践
2009-12-17 16:40 1143目的:使用单点登录系 ... -
Tomcat SSL配置大全
2009-12-17 16:13 1134由于配置CAS的单点登陆系统,需要先配置Tomcat的SSL, ... -
Thread.getContextClassLoader() 详解
2009-12-17 12:24 1206答 : 这个问题经常出现在编写框架代码 , 需要动态加载很多类 ... -
Session知识共享
2009-12-09 17:02 961摘要:虽然session机制在 ... -
跨应用程序的session共享
2009-12-09 15:03 2356原创 跨应用程序的session共享 收藏 常常有这样的情况 ... -
使用HttpURLConnection类(利用sessionId保持会话
2009-12-09 11:19 5559TestServlet.java package n ... -
使用HttpURLConnection获得重定向地址
2009-12-09 09:37 1798今天跟TiGERTiAN谈到HttpURLConnec ... -
java多线程之 wait(),notify(),notifyAll()
2009-12-01 11:38 951java多线程之 wait(),notify(),notify ... -
Callable 和 Future
2009-12-01 11:28 1711天在书上看到callable和future这个两个东东,以前没 ... -
重载和覆盖
2009-11-17 10:02 875override可以翻译为覆盖,从字面就可以知道,它是覆盖了一 ...
相关推荐
在微信小程序的开发中,由于其自身的特性,原生的`wx.request`对象并不支持Cookie的自动发送。这意味着当你在访问需要依赖Cookie进行...在实际开发中,确保正确地处理Cookie,以提供良好的用户体验并保证应用的安全性。
在.NET Web服务中,响应可能包含一个或多个Set-Cookie头部,Java客户端需要正确处理这些头部以保持会话。 在Java中,`HttpURLConnection`或`HttpClient`类提供了处理HTTP请求和响应的能力,包括Cookie管理。对于`...
为了解决这一问题,并确保在跨域场景下可以正确地处理Cookie信息,本文将详细介绍几种常用的跨域访问解决方案及其在处理Cookie方面的应用。 #### 一、跨域访问解决方案概述 跨域访问是指一个域名下的文档或脚本...
4. **处理cookie**:将获取到的cookie保存,后续的请求中都要携带这个cookie,使得服务器认为请求来自已登录用户。 5. **设置请求头**:为了更好地模拟浏览器行为,需要设置User-Agent,以及其他可能的请求头,如...
HTTPClient库,例如Apache HttpClient,支持自动处理Cookie,包括从响应中提取Cookie并在请求中添加。为了处理不同规范之间的冲突,HttpClient可以设置为Cookie兼容模式,以适应Netscape草案、RFC2109和RFC2965等...
3. **响应转发**:当目标服务器返回响应时,代理服务器需要将其转发回原始客户端,同时可能需要处理响应头中的某些字段,例如Set-Cookie和Location,以确保会话和重定向的正确性。 4. **安全性**:代理服务器应具备...
这可能需要对应用中处理cookie和会话的部分进行代码调整和测试,以确保在新的部署环境中能够正常处理跨域请求。 综上所述,通过Nginx配置,可以有效地解决没有域名的环境中,cookie跨域共享的难题。通过精心设计...
在“ShoppingWebsite”这个项目中,你可以根据实际需求,选择上述一种或多种方法来处理cookie跨域问题,确保用户数据的安全传输。同时,理解这些概念和技术对于提升Web应用开发的专业性至关重要。
通过OpenResty,我们可以直接在Nginx中处理Cookie,比如使用`resty.cookie`库来设置Cookie的属性,包括有效期。 例如,要设置Cookie的有效期为24小时,可以使用以下代码: ```lua local cookie = resty_cookie:new...
在Java Web开发中,Servlet是用于处理HTTP请求的核心组件,特别是在服务器端进行业务逻辑处理时。然而,有时候我们在尝试运行一个Servlet时可能会遇到"因缺少代理类而无法加载运行的servlet类"这样的错误,这通常...
在iOS中,处理Cookie主要涉及NSHTTPCookie和NSHTTPCookieStorage。下面分别介绍在UIWebView和WKWebView中操作Cookie的方法: 1. **UIWebView**: - UIWebView自动处理Cookie,只需将Cookie存储在...
本文将重点介绍通过C#后台服务作为代理,处理跨域MVC服务调用并携带Cookie验证的实现。 【C#后台服务代理】 一种常见的解决方案是创建一个C#后台服务,该服务作为一个代理,接收前端的请求,然后转发到目标MVC服务...
要解决这个问题,爬虫需要正确处理Cookie。在请求时,将网站生成的Cookie封装到headers字典中。如果是自动处理,需要通过会话(session)对象捕获和存储Cookie,然后使用携带Cookie的session对象进行请求发送。代码...
此外,还要注意的是,如果使用了代理(例如在Vue CLI中使用`vue.config.js`配置的proxy),在开发环境中,跨域问题通常能得到解决,因为代理服务器会转发请求并处理响应头,使得前端能够正确地携带cookie。...
这一过程中,可能需要处理HTTP头部、编码、cookie等问题。 5. **安全与权限控制**:代理服务器可能会对访问进行限制,例如只允许特定IP地址的请求,或者设置访问频率限制,防止滥用。 6. **日志记录**:为了调试和...
通过上述配置,haproxy在TPROXY模式下,实现了透明代理,使得客户端请求能被正确转发到Server2应用服务器,同时客户端与Server2之间通信的IP包头不会被修改,保持了透明性。同时,iptables规则和内核参数的配置确保...
4. **服务器端处理**:服务器端需要识别来自HTTP和HTTPS的请求,并能正确处理session。例如,可以在接收到HTTP请求时,通过301或302重定向到HTTPS,并在响应中包含session相关的Cookie。 **三、实现过程** 1. **...
1. **Cookie共享**:通过设置相同的Session ID Cookie,在Nginx层面进行会话粘滞性处理,确保请求始终由同一台后端服务器处理。 2. **Session复制**:每个应用服务器都保存所有Session数据,但随着会话数量增加,...
这个配置将所有到达443端口的HTTPS请求转发到https://example.com/,并处理cookie路径问题,确保session正常工作。 而HTTP的反向代理配置类似,但不需要启用SSL: ```apacheconf Listen 80 *:80> ProxyRequests On...