Android中Cookie獲取、保存以及同步

1.先看一下Chrome中的Cookie信息

訪問首頁信息時(shí):

1.第一次調(diào)用的Header信息
2.再次請(qǐng)求時(shí)Header信息

第一次請(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í):

3.直接調(diào)用登錄接口
4.打開首頁后再調(diào)用登錄接口

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)。
如圖:

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)

Cookies.db 表記錄

數(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就是有效的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容