1. Cookie 概述
Cookie是什么? Cookie 是一小段文本信息,伴隨着用戶請求和頁面在 Web 服務器和瀏覽器之間傳遞。Cookie 包含每次用戶訪問站點時 Web 應用程序都可以讀取的信息。
為什么需要Cookie? 因為HTTP協議是無狀態的,對於一個瀏覽器發出的多次請求,WEB服務器無法區分 是不是來源於同一個瀏覽器。所以,需要額外的數據用於維護會話。 Cookie 正是這樣的一段隨HTTP請求一起被傳遞的額外數據。
Cookie能做什么? Cookie只是一段文本,所以它只能保存字符串。而且瀏覽器對它有大小限制以及 它會隨着每次請求被發送到服務器,所以應該保證它不要太大。 Cookie的內容也是明文保存的,有些瀏覽器提供界面修改,所以, 不適合保存重要的或者涉及隱私的內容。
Cookie 的限制。 大多數瀏覽器支持最大為 4096 字節的 Cookie。由於這限制了 Cookie 的大小,最好用 Cookie 來存儲少量數據,或者存儲用戶 ID 之類的標識符。用戶 ID 隨后便可用於標識用戶,以及從數據庫或其他數據源中讀取用戶信息。 瀏覽器還限制站點可以在用戶計算機上存儲的 Cookie 的數量。大多數瀏覽器只允許每個站點存儲 20 個 Cookie;如果試圖存儲更多 Cookie,則最舊的 Cookie 便會被丟棄。有些瀏覽器還會對它們將接受的來自所有站點的 Cookie 總數作出絕對限制,通常為 300 個。
2. HttpClient4.2支持的Cookie策略
Netscape標准
Netscape是最原始的Cookies規范,同時也是RFC2109的基礎。盡管如此,還是在很多重要的方面與RFC2109不同,可能需要特定服務器才可以兼容。
RFC2109
RFC2109是W3C組織第一次推出的官方Cookies標准。理論上,所有使用版本1Cookies的服務端都應該使用此標准。HttpClient已經將此標准設定為默認。
遺憾的是,許多服務端不正確的實現了標准或者仍然使用Netscape標准。所有有時感到此標准太多於嚴格。
RFC2965
RFC2965定義了版本2並且嘗試去彌補在版本1中Cookie的RFC2109標准的缺點。RFC2965是,並規定RFC2965最終取代RFC2109.
發送RFC2965標准Cookies的服務端,將會使用Set-Cookie2 header添加到Set-Cookie Header信心中,RFC2965 Cookies是區分端口的。
Browser Compatibility
這種兼容性設計要求是適應盡可能多的不同的服務器,盡管不是完全按照標准來實現的。如果你遇到了解析Cookies的問題,你就可能要用到這一個規范。
有太多的web站點是用CGI腳本去實現的,而導致只有將所有的Cookies都放入Request header才可以正常的工作。這種情況下最好設置http.protocol.single-cookie-header參數為true。
Best match
'Meta' cookie specification that picks up a cookie policy based on the format of cookies sent with the HTTP response. It basically aggregates all above implementations into one class.
Ignore Cookies
此規格忽略所有Cookie 。被用來防止HttpClient接受和發送的Cookie。
強烈建議使用Best match策略讓HttpClient根據執行環境選擇一個合適級別的策略。選擇Cookie策略示范代碼:
import org.apache.http.client.HttpClient; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.params.ClientPNames; import org.apache.http.client.params.CookiePolicy;
HttpClient httpclient = new DefaultHttpClient(); // force strict cookie policy per default httpclient.getParams().setParameter( ClientPNames.COOKIE_POLICY, CookiePolicy.RFC_2965); HttpGet httpget = new HttpGet("http://www.broken-server.com/"); // Override the default policy for this request httpget.getParams().setParameter( ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY);
3. HttpClient登錄服務器后Cookie讀取不全,通過IE瀏覽器登錄則可以的問題
解決:
正常情況是因為HttpClient沒有打開與IE相同的頁面導致的。即IE曾經打開了哪些頁面(通常是登錄頁面,也可能是會員頁面等),HttpClient也必須打開同樣的頁面,否則它的Cookie就可能不全。最好是通過Fidder觀察在瀏覽器中的操作與通過HttpClient操作有何差別,找出差別,然后予以解決。
Fiddler的使用參考:Fiddler教程(轉);
在代碼中設置代理服務器,參考:HttpClient4.2在Java中的幾則應用:Get、Post參數、Session(會話)保持、Proxy(代理服務器)設置,多線程設置...
了解HTTP一些基礎知識,參考:HTTP協議概述
通過Fiddler不僅可以解決上述問題,其實只要是IE瀏覽器能夠做的事情,HttpClient如果做不了,那么就通過Fiddler來分析所有通訊數據,找出它們的差別,然后予以解決。
示范(自定義Cookie):
private HttpClient m_httpClient = new DefaultHttpClient();
private boolean login2Site() throws Exception { // 代碼略 }
// 抓取頁面 private String getHTMLPage(String url) throws Exception { String result = "";
HttpGet httpget = new HttpGet(url); httpget.setHeader("Referer", "http://www.yoursite.com/"); httpget.setHeader("Cookie", getCookies()); HttpResponse response = m_httpClient.execute(httpget); HttpEntity entity = response.getEntity(); result = EntityUtils.toString(entity, "GBK"); httpget.releaseConnection();
return result; } private String getCookies() { StringBuilder sb = new StringBuilder(); List<Cookie> cookies = ((AbstractHttpClient)m_httpClient).getCookieStore().getCookies(); for(Cookie cookie: cookies) sb.append(cookie.getName() + "=" + cookie.getValue() + ";"); // 除了HttpClient自帶的Cookie,自己還可以增加自定義的Cookie // 增加代碼...
return sb.toString(); }
功能強大的Fiddler
本文參考:
http://www.cnblogs.com/fish-li/archive/2011/07/03/2096903.html
http://blog.csdn.net/ronghua_liu/article/details/8105463
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/statemgmt.html