1.先看一下Chrome中的Cookie信息
訪問首頁信息時:
第一次請求時,服務器會在Response Header中帶過來一個Set-Cookie,內容為JSESSIONID=C3E668A891BA4E1C160C91A7EBEAA540.h2;
再次請求時,瀏覽器會在Requst Header中的Cookie將JSESSIONID帶上。
在瀏覽器中調用登錄接口時:
2.項目中實際問題
例如在一個項目中,登錄界面為原生開發,而一些訂單預定處理頁面使用WebView嵌套Html5頁面。結合實際開發,主要碰到了兩個問題:
1.項目中使用的Volley作為網絡請求庫,默認 只能獲取的respone header中的各類的第一條數據,如圖3中,會有兩條Set-Cookie,而Volley默認只會拿到第一條Set-Cookie,也就是只能拿到JSESSIONID的值,不會拿到tocken的值;
2.默認情況下,每一次的原生接口調用就會產生新的JSESSIONID(因為默認請求中沒有同步cookie),調用登錄接口時如何同步最新的JSESSIONID?
3.WebView中Cookie同步機制
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) {
/*看這里...默認只獲取index=0的值*/
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
}
2.在發送請求的時候帶上最新的JSESSIONID(請求頭里添加Cookie),volley設置請求頭,代碼如下:
@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:在調用登錄接口時同步最新的JSESSIONID。最初的解決辦法:會在剛進入APP時主動調用某一個接口,獲取JSESSIONID,并做保存,在后續的請求上會一直使用這個JSESSIONID。這個辦法最初竟然可以順暢的使用,可是后續發現Cookie會 產生新的內容,比如說有的接口會更新tocken,每次就需要手動單獨去更新tocken,這樣做很不利于管理。
現在的做法是,在Volley解析數據的時候拿到Cookie(以"Set-Cookie"作為Key值),并將Cookie保存到CookieManager中,當然如果已經有保存的Cookie,在請求的時候也要帶上,這樣就做到了原生請求的時候Cookie的同步,然后在調用WebView時,只需要從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機制
WebView是基于webkit內核的UI控件,相當于一個瀏覽器客戶端。它會在本地維護每次會話的cookie(保存在data/data/package_name/app_WebView/Cookies.db)。
如圖:
當WebView加載URL的時候,WebView會從本地讀取該URL對應的cookie,并攜帶該cookie與服務器進行通信。WebView通過android.webkit.CookieManager類來維護cookie。CookieManager是 WebView的cookie管理類。
之前同步cookie需要用到CookieSyncManager類,現在這個類已經被deprecated。如今WebView已經可以在需要的時候自動同步cookie了,所以不再需要創建CookieSyncManager類的對象來進行強制性的同步cookie了。現在只需要獲得 CookieManager的對象將cookie設置進去就可以了。
CookieManager的使用方法
1.setCookie(String url, String cookie)
2.getCookie(String url)
數據庫會根據name、host、path等生成一條記錄,也即是說在CookieManager#setCookie()中name、host和path一致會導致覆蓋原來的記錄。
ps:調用SetCookie()方法時,如果一條Set-Cookie中包含JSESSIONID和tocken,Cookies.db對應的表中會插入兩天記錄,name分別為JSESSIONID和tocken;如果以圖3為例,會有兩條Set-Cookie,如果按照順序把兩條拼接,在Cookies.db的表中只會插入一條name為JSESSIONID的記錄,這種時候可以調用兩次setCookie()方法。
Cookie相關的Http頭
有 兩個Http頭部和Cookie有關:Set-Cookie和Cookie。
Set-Cookie由服務器發送,它包含在響應請求的頭部中。它用于在客戶端創建一個Cookie
Cookie頭由客戶端發送,包含在HTTP請求的頭部中。注意,只有cookie的domain和path與請求的URL匹配才會發送這個cookie。
Set-Cookie響應頭的格式如下所示:
Set-Cookie: <name>=<value>[; <name>=<value>]...
[; expires=<date>][; domain=<domain_name>]
[; path=<some_path>][; secure][; httponly]
expires=<date>: 設置cookie的有效期,如果cookie超過date所表示的日期時,cookie將失效。
如果沒有設置這個選項,那么cookie將在瀏覽器關閉時失效。
secure : 表示cookie只能被發送到http服務器。
httponly : 表示cookie不能被客戶端腳本獲取到。
注:臨時cookie(沒有expires參數的cookie)不能帶有domain選項。
當客戶端發送一個http請求時,會將有效的cookie一起發送給服務器。
如果一個cookie的domain和path參數和URL匹配,那么這個cookie就是有效的。