1.先看一下Chrome中的Cookie信息
訪問首頁信息時(shí):


第一次請(qǐng)求時(shí),服務(wù)器會(huì)在Response Header中帶過來一個(gè)Set-Cookie,內(nèi)容為JSESSIONID=C3E668A891BA4E1C160C91A7EBEAA540.h2;
再次請(qǐng)求時(shí),瀏覽器會(huì)在Requst Header中的Cookie將JSESSIONID帶上。
在瀏覽器中調(diào)用登錄接口時(shí):


2.項(xiàng)目中實(shí)際問題
例如在一個(gè)項(xiàng)目中,登錄界面為原生開發(fā),而一些訂單預(yù)定處理頁面使用WebView嵌套Html5頁面。結(jié)合實(shí)際開發(fā),主要碰到了兩個(gè)問題:
1.項(xiàng)目中使用的Volley作為網(wǎng)絡(luò)請(qǐng)求庫,默認(rèn) 只能獲取的respone header中的各類的第一條數(shù)據(jù),如圖3中,會(huì)有兩條Set-Cookie,而Volley默認(rèn)只會(huì)拿到第一條Set-Cookie,也就是只能拿到JSESSIONID的值,不會(huì)拿到tocken的值;
2.默認(rèn)情況下,每一次的原生接口調(diào)用就會(huì)產(chǎn)生新的JSESSIONID(因?yàn)槟J(rèn)請(qǐng)求中沒有同步cookie),調(diào)用登錄接口時(shí)如何同步最新的JSESSIONID?
3.WebView中Cookie同步機(jī)制
3.解決問題
問題1:可以有兩種解決方案
1.修改Volley源碼:Volley#toolbox#HurlStack.java#performRequest()
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
...
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
...
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
/*看這里...默認(rèn)只獲取index=0的值*/
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
}
2.在發(fā)送請(qǐng)求的時(shí)候帶上最新的JSESSIONID(請(qǐng)求頭里添加Cookie),volley設(shè)置請(qǐng)求頭,代碼如下:
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
String cookie = CookieManager.getInstance().getCookie(Constant.BASE_URL);
sendHeader.put("Cookie", cookie);
if(sendHeader != null) {
return sendHeader;
}
return super.getHeaders();
}
問題2:在調(diào)用登錄接口時(shí)同步最新的JSESSIONID。最初的解決辦法:會(huì)在剛進(jìn)入APP時(shí)主動(dòng)調(diào)用某一個(gè)接口,獲取JSESSIONID,并做保存,在后續(xù)的請(qǐng)求上會(huì)一直使用這個(gè)JSESSIONID。這個(gè)辦法最初竟然可以順暢的使用,可是后續(xù)發(fā)現(xiàn)Cookie會(huì) 產(chǎn)生新的內(nèi)容,比如說有的接口會(huì)更新tocken,每次就需要手動(dòng)單獨(dú)去更新tocken,這樣做很不利于管理。
現(xiàn)在的做法是,在Volley解析數(shù)據(jù)的時(shí)候拿到Cookie(以"Set-Cookie"作為Key值),并將Cookie保存到CookieManager中,當(dāng)然如果已經(jīng)有保存的Cookie,在請(qǐng)求的時(shí)候也要帶上,這樣就做到了原生請(qǐng)求的時(shí)候Cookie的同步,然后在調(diào)用WebView時(shí),只需要從CookieManager中同步即可。可以在Volley的Request中處理:JsonObjectRequest#parseNetworkResponse()
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString =
new String(response.data, HttpHeaderParser.parseCharset(response.headers));
mHeader = response.headers.toString();
JSONObject jsonObject = new JSONObject(jsonString);
for(Map.Entry<String,String> entry : response.headers.entrySet()) {
if("Set-Cookie".equalsIgnoreCase(entry.getKey())) {
//將cookie保存到CookieManager
CookieManager.getInstance().setCookie(Constant.BASE_URL, entry.getValue());
break;
}
}
return Response.success(jsonObject,
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
3.WebView中的Cookie機(jī)制
WebView是基于webkit內(nèi)核的UI控件,相當(dāng)于一個(gè)瀏覽器客戶端。它會(huì)在本地維護(hù)每次會(huì)話的cookie(保存在data/data/package_name/app_WebView/Cookies.db)。
如圖:

當(dāng)WebView加載URL的時(shí)候,WebView會(huì)從本地讀取該URL對(duì)應(yīng)的cookie,并攜帶該cookie與服務(wù)器進(jìn)行通信。WebView通過android.webkit.CookieManager類來維護(hù)cookie。CookieManager是 WebView的cookie管理類。
之前同步cookie需要用到CookieSyncManager類,現(xiàn)在這個(gè)類已經(jīng)被deprecated。如今WebView已經(jīng)可以在需要的時(shí)候自動(dòng)同步cookie了,所以不再需要?jiǎng)?chuàng)建CookieSyncManager類的對(duì)象來進(jìn)行強(qiáng)制性的同步cookie了?,F(xiàn)在只需要獲得 CookieManager的對(duì)象將cookie設(shè)置進(jìn)去就可以了。
CookieManager的使用方法
1.setCookie(String url, String cookie)
2.getCookie(String url)

數(shù)據(jù)庫會(huì)根據(jù)name、host、path等生成一條記錄,也即是說在CookieManager#setCookie()中name、host和path一致會(huì)導(dǎo)致覆蓋原來的記錄。
ps:調(diào)用SetCookie()方法時(shí),如果一條Set-Cookie中包含JSESSIONID和tocken,Cookies.db對(duì)應(yīng)的表中會(huì)插入兩天記錄,name分別為JSESSIONID和tocken;如果以圖3為例,會(huì)有兩條Set-Cookie,如果按照順序把兩條拼接,在Cookies.db的表中只會(huì)插入一條name為JSESSIONID的記錄,這種時(shí)候可以調(diào)用兩次setCookie()方法。
Cookie相關(guān)的Http頭
有 兩個(gè)Http頭部和Cookie有關(guān):Set-Cookie和Cookie。
Set-Cookie由服務(wù)器發(fā)送,它包含在響應(yīng)請(qǐng)求的頭部中。它用于在客戶端創(chuàng)建一個(gè)Cookie
Cookie頭由客戶端發(fā)送,包含在HTTP請(qǐng)求的頭部中。注意,只有cookie的domain和path與請(qǐng)求的URL匹配才會(huì)發(fā)送這個(gè)cookie。
Set-Cookie響應(yīng)頭的格式如下所示:
Set-Cookie: <name>=<value>[; <name>=<value>]...
[; expires=<date>][; domain=<domain_name>]
[; path=<some_path>][; secure][; httponly]
expires=<date>: 設(shè)置cookie的有效期,如果cookie超過date所表示的日期時(shí),cookie將失效。
如果沒有設(shè)置這個(gè)選項(xiàng),那么cookie將在瀏覽器關(guān)閉時(shí)失效。
secure : 表示cookie只能被發(fā)送到http服務(wù)器。
httponly : 表示cookie不能被客戶端腳本獲取到。
注:臨時(shí)cookie(沒有expires參數(shù)的cookie)不能帶有domain選項(xiàng)。
當(dāng)客戶端發(fā)送一個(gè)http請(qǐng)求時(shí),會(huì)將有效的cookie一起發(fā)送給服務(wù)器。
如果一個(gè)cookie的domain和path參數(shù)和URL匹配,那么這個(gè)cookie就是有效的。