日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

HTTP代理如何正确处理Cookie(2) - 转载(写的比较详细,并以实例讲解)

發(fā)布時(shí)間:2025/3/20 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HTTP代理如何正确处理Cookie(2) - 转载(写的比较详细,并以实例讲解) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

HTTP代理如何正確處理Cookie(2) - 轉(zhuǎn)載(寫的比較詳細(xì),并以實(shí)例講解) http://hi.baidu.com/bdui/blog/item/de891ad90cb4b12f11df9ba1.html 2007-12-09 19:38

解決問(wèn)題的兩種思路

Cookie的存在是要解決HTTP協(xié)議本身先天的缺陷-無(wú)狀態(tài)性,它為用戶保存了一些需要的狀態(tài)信息。因此我們解決此問(wèn)題的最本質(zhì)的出發(fā)點(diǎn),也就是找到一種途徑能為用戶保存Cookie所提供用戶狀態(tài)信息,實(shí)際上就是Name/Value對(duì)。

思路一

第一種思路就是修改目標(biāo)服務(wù)器取得的Cookie,使之符合MTS站點(diǎn)的屬性,然后作為MTS站點(diǎn)的Cookie存儲(chǔ)到用戶的瀏覽器中去。當(dāng)然,這 種修改必須保留原始Cookie的所有屬性值,當(dāng)以后訪問(wèn)同一個(gè)目標(biāo)服務(wù)器的時(shí)候,MTS能根據(jù)保存的屬性值還原出原始Cookie,然后進(jìn)行提交。

具體到屬性值的保存位置,沒有太多選擇的余地,實(shí)際上,domain,path,secure,expires這幾個(gè)屬性都無(wú)法利用,只有利用 name=value這一屬性對(duì)。我們的做法是創(chuàng)造一個(gè)新的Cookie,把原始Cookie的domain,path的值與name值進(jìn)行編碼,用分隔 符附加在Name值的后面,符值給新的Cookie。這樣做也同時(shí)避免了不同目標(biāo)服務(wù)器如果出現(xiàn)同名的Cookie,將會(huì)互相覆蓋的情況(Cookie規(guī) 范里面也規(guī)定了,客戶端以domain,path,name作為Cookie的唯一標(biāo)示)。而原始Cookie的secure和expires值,直接符 給新的Cookie,新Cookie的domain和path設(shè)成缺省值,這樣,新Cookie就可以被瀏覽器正常接受。由于瀏覽器接受的所有 Cookie的domain和path值都一樣,因此每次用戶對(duì)MTS提出請(qǐng)求時(shí),瀏覽器都會(huì)把所有與MTS站點(diǎn)相關(guān)的Cookie上傳,因此,MTS還 需要還原原始的Cookie,過(guò)濾掉與目標(biāo)服務(wù)器不相干的Cookie,然后上傳有用的Cookie。

這種思路的優(yōu)點(diǎn)在于Cookie存儲(chǔ)在客戶端,可以做到長(zhǎng)期存儲(chǔ),瀏覽器自己根據(jù)Cookie的expires值做出判斷,省掉很多開發(fā)的麻煩。缺 點(diǎn)是轉(zhuǎn)換的過(guò)程相對(duì)較復(fù)雜。另外還有一個(gè)缺點(diǎn),也是由于Cookie規(guī)范的限制所造成的。Cookie規(guī)范對(duì)于一個(gè)瀏覽器同時(shí)能夠存儲(chǔ)的Cookie數(shù)量 作出了規(guī)定。

  • 總共300 個(gè)cookie
  • 每個(gè)Cookie 4 K 的存儲(chǔ)容量
  • 每一個(gè)domain 或者 server 20 個(gè)cookie。

以上是瀏覽器所應(yīng)達(dá)到的最小存儲(chǔ)數(shù)量,超出這個(gè)限制,瀏覽器應(yīng)該自動(dòng)按照最少最近被使用的原則刪除超出得Cookie。由于用戶有可能通過(guò)MTS這 一個(gè)網(wǎng)站翻譯大量的目標(biāo)服務(wù)器,因此瀏覽器存儲(chǔ)在MTS的domain下的cookie數(shù)量就很有可能超過(guò)20個(gè),這時(shí)候就會(huì)導(dǎo)致某些Cookie被刪 除。一般這也不會(huì)造成太大問(wèn)題,因?yàn)橐?guī)范是要求瀏覽器刪除最少最近被使用的Cookie,但我們?cè)趯?shí)際測(cè)試當(dāng)中發(fā)現(xiàn)有些瀏覽器并不遵守這樣的規(guī)范,而是刪 除最新的Cookie,這就將導(dǎo)致用戶很大的不便。

思路二

第二種思路在于把原始的Cookie組織成dataBean,存儲(chǔ)到用戶的Session當(dāng)中去。這樣,在用戶端只需要存儲(chǔ)一個(gè)SessionID 的Cookie,而不需要存儲(chǔ)所有目標(biāo)服務(wù)器的每一個(gè)Cookie。另外,當(dāng)接收到用戶的又一次翻譯請(qǐng)求時(shí),再?gòu)腟ession當(dāng)中取出所有的 dataBean,逐一進(jìn)行分析,找出與用戶所請(qǐng)求的目標(biāo)服務(wù)器相符的原始Cookie,進(jìn)行提交。

這種思路可以克服上一種思路中Cookie超過(guò)標(biāo)準(zhǔn)數(shù)量時(shí)的缺陷,而且不需編碼保存原始的Cookie屬性值,減少了程序的復(fù)雜度。缺點(diǎn)是需要程序 員自己處理expires。而且由于是把Cookie存儲(chǔ)在Session中,一旦Session失效,所有Cookie都將被刪除,所以,無(wú)法保存那些 長(zhǎng)期的Cookie。

總之,兩種思路各有利弊,在實(shí)際應(yīng)用當(dāng)中要權(quán)衡考慮。下面我們針對(duì)兩種思路進(jìn)行技術(shù)實(shí)現(xiàn),分別對(duì)應(yīng)方案一和方案二。

由于MTS需要與目標(biāo)服務(wù)器連接,遵循HTTP協(xié)議讀取和返回Cookie,但是如果用JDK中的java.net.URLConnection處理Cookie將非常不方便,因此我們使用HTTPClient來(lái)處理與目標(biāo)服務(wù)器的連接。

?

方案一:Cookie存儲(chǔ)在瀏覽器端

用戶每發(fā)起一次新的請(qǐng)求,瀏覽器在檢查完本地存儲(chǔ)Cookie的有效性后,會(huì)把所有由MTS產(chǎn)生的有效Cookie附加在請(qǐng)求頭里送到MTS。 MTS接受到客戶端的翻譯請(qǐng)求后,從Request中提取出所有的Cookie,還原后根據(jù)目標(biāo)服務(wù)器的domain和path進(jìn)行過(guò)濾。產(chǎn)生所有與目標(biāo) 服務(wù)器相關(guān)的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();
//用正則表達(dá)式把name項(xiàng)還原成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用來(lái)存儲(chǔ)將被傳到目標(biāo)服務(wù)器的Cookie
Cookie[] transferedCookie = new Cookie[cookiesList.size()];
cookiesList.toArray(transferedCookie);

接下來(lái),需要把Cookie送到目標(biāo)服務(wù)器中。我們使用HTTPClient與目標(biāo)服務(wù)器連接。HTTPClient在與目標(biāo)服務(wù)器連接以后,允許 服務(wù)器設(shè)置Cookie并在需要的時(shí)候自動(dòng)將Cookie返回服務(wù)器,也支持手工設(shè)置 Cookie后發(fā)送到服務(wù)器端。但是,由于如何處理cookie有幾個(gè)規(guī)范互相沖突:Netscape Cookie 草案、RFC2109、RFC2965,而且還有很大數(shù)量的軟件商的Cookie實(shí)現(xiàn)不遵循任何規(guī)范。 為了處理這種狀況,需要把HttpClient設(shè)置成Cookie兼容模式,這樣可以最大限度的處理好各種Cookie。下面的代碼把Cookie送到目 標(biāo)服務(wù)器。


HttpClient client = new HttpClient();
//從request得到所有需要傳輸?shù)腸ookie
Cookie[] questCookie = getCookieFromRequest(request);
//設(shè)置HTTPClient為Cookie兼容模式
client.getState().setCookiePolicy(CookiePolicy.COMPATIBILITY);
if (questCookie.length > 0)
//把Cookie加到httpclient中
client.getState().addCookies(questCookie);
HttpMethod method = new GetMethod(TagerURL);
//向目標(biāo)服務(wù)器發(fā)送請(qǐng)求
int statusCode = client.executeMethod(method);
method.releaseConnection();

MTS把請(qǐng)求和Cookie送出后,繼續(xù)接收目標(biāo)服務(wù)器的應(yīng)答,讀取返回的原始Cookie,并轉(zhuǎn)換成可以存儲(chǔ)在用戶瀏覽器端的Cookie。下面 的代碼將對(duì)原始Cookie的內(nèi)容進(jìn)行變換,保留expires和secure等項(xiàng),把domain和path項(xiàng)編碼到name中去。


//從HTTPClient中取得所有的Cookie
Cookie[] temp = client.getState().getCookies();
if (temp != null)
{
javax.servlet.httpCookie theCookie = new javax.servlet.http.Cookie[temp.length];
//逐一對(duì)Cookie進(jìn)行處理
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());
//復(fù)制其他項(xiàng)
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應(yīng)答頭返回用戶瀏覽器。并保存在瀏覽器中。


//把所有轉(zhuǎn)換后的Cookie加入response
for (int i = 0; i < theCookie.length; i++) {
response.addCookie(theCookie[i]);
}

至此,我們已經(jīng)完成了接收用戶請(qǐng)求,轉(zhuǎn)換Cookie,發(fā)送到目標(biāo)服務(wù)器,接收目標(biāo)服務(wù)器的原始Cookie,并保存在客戶瀏覽器的整個(gè)處理過(guò)程。

方案二:Cookie存儲(chǔ)在服務(wù)器端

在此種方案中,目標(biāo)服務(wù)器返回給MTS的Cookie將被組織成dataBean,存儲(chǔ)在用戶的Session中。因此,我們首先生成一個(gè)用來(lái)存儲(chǔ) Cookie的類CookiesBean,根據(jù)它的特性,它可以繼承ArraryList類。此對(duì)象將存儲(chǔ)用戶訪問(wèn)目標(biāo)服務(wù)器時(shí)接收到的所有 Cookie,并提供與新接收到的Cookie融合的功能,同時(shí)能夠刪除過(guò)期的Cookie,更新同名的Cookie。


public class CookiesBean extends ArrayList
{
/**
* 處理Cookies.
* @參數(shù) 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]);
}
}
}
/**
* 融合參數(shù)中的bean
* @參數(shù) bean
* 參考: rfc2109 4.3.3 Cookie Management
*/
public void RefreshBean(CookiesBean bean)
{
if (bean == null)
return;
Iterator it = bean.iterator();
//針對(duì)bean中的每一個(gè)Cookie進(jìn)行處理
while (it.hasNext())
{
Cookie beanCookie = (Cookie) it.next();
if (beanCookie == null) continue;
ArrayList drop = new ArrayList();
Iterator thisIt = iterator();
//取出存儲(chǔ)的Cookie進(jìn)行比較和處理
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;
}
//刪除過(guò)期的Cookie
if (thisCookie.isExpired())
drop.add(thisCookie);
}
//刪除所有drop中的Cookie
this.removeAll(drop);
//如果beanCookie有效,則加入到存儲(chǔ)區(qū)中。
if (!beanCookie.isExpired())
add(beanCookie);
}
return;
}
}

當(dāng)MTS接受到客戶端的翻譯請(qǐng)求后,會(huì)從Session中提取出所有的dataBean,并得到存儲(chǔ)的所有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在所有的存儲(chǔ)的Cookie中,檢查Cookie的Domain、path和secure的值,篩選出符合目標(biāo)服務(wù)器的Cookie。


//提取目標(biāo)服務(wù)器的domain和path
String url = context.getURL();
String domain = URLUtil.getURLHost(url);
String path = url.substring(domain.length());

String cookiedomain = null;
String cookiepath = null;
//逐個(gè)比較Cookie的domain和path
//把符合要求的Cookie紀(jì)錄到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送到目標(biāo)服務(wù)器的代碼與方案一基本一樣,在此忽略。

最后一步,需要把Cookie存儲(chǔ)到Session中。下面的代碼將從目標(biāo)服務(wù)器接受Cookie,融入到dataBean中,并保存到客戶的Session中。


//從目標(biāo)服務(wù)器得到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);
//目標(biāo)服務(wù)器端的Cookie融合到Session中的dataBean中
dataBean.RefreshBean(bean);
}
//把最終的dataBean存入Session中
session.setAttribute(SESSION_NAME, dataBean);
}

至此,我們已經(jīng)完成了在Session中保存?zhèn)€目標(biāo)服務(wù)器所產(chǎn)生Cookie的整個(gè)處理過(guò)程。


?

關(guān)于Session的考慮

在研究完如何管理和傳遞Cookie之后,我們也需要研究一下Session的傳遞。因?yàn)槟壳按蟛糠终军c(diǎn)都在采用Session機(jī)制保存用戶狀態(tài)數(shù)據(jù),如果不能解決Session的傳遞問(wèn)題,HTTP應(yīng)用代理服務(wù)器的適用范圍同樣會(huì)大打折扣。

首先我們了解一下Session的實(shí)現(xiàn)機(jī)制。Session是一種服務(wù)器端的機(jī)制,服務(wù)器使用一種類似于散列表的結(jié)構(gòu)來(lái)保存信息。當(dāng)程序需要為某個(gè) 客戶端的請(qǐng)求創(chuàng)建一個(gè)session的時(shí)候,服務(wù)器首先檢查這個(gè)客戶端的請(qǐng)求里是否已包含了一個(gè)session標(biāo)識(shí) - 稱為session id,如果已包含一個(gè)session id則說(shuō)明以前已經(jīng)為此客戶端創(chuàng)建過(guò)session,服務(wù)器就按照session id把這個(gè)session檢索出來(lái)使用(如果檢索不到,可能會(huì)新建一個(gè)),session id的值應(yīng)該是一個(gè)既不會(huì)重復(fù),又不容易被找到規(guī)律以仿造的字符串。

保存這個(gè)session id的方式之一就是采用Cookie。一般這個(gè)Cookie的名字都類似于 SESSIONID。比如WebSphere對(duì)于Web應(yīng)用程序生成的Cookie:JSESSIONID= 0001HWF4iVD94pY8Cpbx6U4CXkf:10lro0398,它的名字就是 JSESSIONID。

保存session id的其他方式還包括URL重寫和表單隱藏字段。這兩種方式都不需要代理服務(wù)器作特殊處理。因此實(shí)際上,我們解決了Cookie的管理和傳遞的問(wèn)題之后,也就解決了Session的管理和傳遞。


結(jié)束語(yǔ)

從上面的討論中可以看出,由于Cookie本身的規(guī)范限制,HTTP應(yīng)用代理所必需面對(duì)的一個(gè)問(wèn)題就是如何對(duì)Cookie進(jìn)行正確的處理。本文對(duì)此 提出了兩種解決思路并列出了實(shí)現(xiàn)代碼。對(duì)于MTS項(xiàng)目本身,我們使用的是第二種方案。開發(fā)人員在認(rèn)識(shí)好Cookie本身的特性之后,參照本文的思路,根據(jù) 自己系統(tǒng)的特點(diǎn),也會(huì)找出更適宜的解決方案。



參考資料

  • Netscape Cookie Specification 對(duì)Netscape Cookie 使用的特性進(jìn)行了簡(jiǎn)要的介紹。
  • RFC2965:HTTP State Management Mechanism 介紹了HTTP 狀態(tài)管理機(jī)制
  • RFC2109 w3c發(fā)布的第一個(gè)官方cookie規(guī)范
  • RFC2616:Hypertext Transfer Protocol 超文本傳輸協(xié)議
  • Ronald Tschalr開發(fā)了 HTTPClient,將其作為 URLConnection 的替代品。
  • Jakarta Regexp Apache 的開源項(xiàng)目,處理正則表達(dá)式的java包。

?


總結(jié)

以上是生活随笔為你收集整理的HTTP代理如何正确处理Cookie(2) - 转载(写的比较详细,并以实例讲解)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。