簡介
Session是服務端驗證客戶端身份的一種機制喂击。而Cookie是客戶端存儲的一種身份憑證屠列,由服務端在回應的消息頭中通過Set-Cookie字段“種”在客戶端萤厅。以后每次客戶端在向服務端請求時都會在消息頭中帶上Cookie字段入录。服務端就會根據(jù)Cookie的來判斷此次請求是從哪個用戶發(fā)過來的来涨,是否是一次有效請求等塔橡。有關Cookie的標準定義可以參考 RFC6265梅割。以下我們稍做介紹。
舉個例子
首次打開瀏覽器請求http://www.baidu.com 葛家,我們會得到一個response消息頭如下格式户辞。
可以看到里面包含了多條Set-Cookie,Cookie是key-value形式的癞谒。每條Cookie都有一個效信息字段底燎,有些還含有expires、domain和path等字段扯俱。其中的domain和path字段书蚪,區(qū)分了不同的cookie可以被哪些網(wǎng)頁鏈接獲得,如未設置domain迅栅,則使用當前的訪問鏈接替代殊校。比如上面的BD_HOME=0,我們在下表[本地cookie信息]中读存,一樣可以看到它的domain為www.baidu.com为流,這是因為我們訪問的就是這個地址呕屎。其中的expires和max-age定義了該Cookie的有效期,失效的Cookie在下次請求時不會被加入到Cookie頭中敬察。
請求后我們查看瀏覽器的Cookie表秀睛,可以看到這些Cookie信息被“種”下了。
此時再發(fā)送一條請求莲祸,可以看到請求的消息頭如下:
不同網(wǎng)站的請求蹂安,會因為請求鏈接的不同,只能從瀏覽器取得屬于自己的cookie锐帜,根據(jù)上文提到的domain和path字段來區(qū)分田盈。關于匹配的詳細規(guī)則還是可以查看RFC6265掀虎。這邊我們用domain的匹配來稍微說下智政,在標準的5.1.3條目是有關domain matching
的。
A string domain-matches a given domain string if at least one of the
following conditions hold:
o The domain string and the string are identical. (Note that both
the domain string and the string will have been canonicalized to
lower case at this point.)
o All of the following conditions hold:
* The domain string is a suffix of the string.
* The last character of the string that is not included in the
domain string is a %x2E (".") character.
* The string is a host name (i.e., not an IP address).
從中我們可以看出domain要滿足兩點才能算是匹配射众,第一點蛮拔,完全相同述暂;如果第一點不滿足則需要,請求鏈接的后綴包含存儲的cookie的domain建炫,并且不包含部分的最后一個字符還得是"."畦韭,并且還是是個域名,而非IP地址踱卵。domain匹配成功后廊驼,還要匹配path据过,path的匹配要比domain復雜一些惋砂,具體可以查看標準的5.1.4。path匹配可以做到一個網(wǎng)站下的頁面可以分別存儲不同的cookie绳锅,也可以共享上層父頁面的cookie西饵,比較靈活。
客戶端實現(xiàn)
Android自身所帶的HttpUrlConnection方法是默認不開啟Cookie存儲的鳞芙。不過我們可以用java提供的幾個類來在Android中實現(xiàn):
可以先在所有請求之前聲明
CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
開啟此開關后眷柔,每次請求的Set-Cookie信息都會被CookieManager處理。CookieManager又會使用第一個參數(shù)傳入的CookieStore來處理Cookie的存儲問題原朝,因為此處傳入了null驯嘱,系統(tǒng)會默認調(diào)用一個基于CookieStore實現(xiàn)的CookieStoreImpl類來處理Cookie的存儲,這個類的只有基于內(nèi)存的存儲喳坠,當進程被殺死后鞠评,下次再進入應用,保存的Cookie信息就會丟失壕鹉。所以我們需要基于CookieStore這個接口實現(xiàn)一個具有內(nèi)存和本地雙存儲機制的Cookie存儲類剃幌。
可以參考:Fran Montiel實現(xiàn)的PersistentCookieStore 類:
https://gist.github.com/franmontiel/ed12a2295566b7076161
當解決了Cookie的存儲后聋涨,我們就需要考慮以后我們的每次請求需要在請求的消息頭中加入Cookie字段。以上用CookieStore存儲下來的Cookie信息都會被保存成HttpCookie形式的信息负乡。我們可以上面的百度例子中看到Cookie的組成樣式牍白,所以我們可以提取CookieStore中的信息并組合。
StringBuilder cookieBuilder = new StringBuilder();
String divider = "";
for (HttpCookie cookie : getCookies()) {
cookieBuilder.append(divider);
divider = ";";
cookieBuilder.append(cookie.getName());
cookieBuilder.append("=");
cookieBuilder.append(cookie.getValue());
}
cookieString = cookieBuilder.toString();
然后把這個cookieString在以后的請求中加入到頭中抖棘,如果你用HttpUrlConnection茂腥,你就可以
setRequestProperty("Cookie",cookieString);
如果你需要讓應用中打開的WebView頁面也能共享使用Cookie,則需要使用android.webkit.CookieManager
類來設置切省,簡單式例代碼如下础芍。注意,第一個參數(shù)要使用鏈接的host部分数尿。這樣讓web端的不同頁面也可以共享這些cookie仑性。
for (HttpCookie cookie : getCookies()) {
CookieManager.getInstance().setCookie(Uri.parse(url).getHost(), cookie.toString());
}
OK,大功告成右蹦。
以下是我在Github上開源的一個基于Volley實現(xiàn)的網(wǎng)絡層框架诊杆,也包括Cookie機制的Http請求,歡迎大家fork:
https://github.com/CPPAlien/DaVinci
作者簡介
彭濤(@彭濤me) 致力于讓技術變得易懂且有趣
個人博客:http://pengtao.me, GitHub地址:https://github.com/CPPAlien