Android 接口請求添加cookie和webview中cookie、UA、Header等相關設置

前言

服務端可能需要根據cookie值判定是否有登錄、灰度環境、獲取相關有效信息等用于區分當前用戶。

HttpClient請求添加Cookie

現在大多數項目估計都是使用的okhttp網絡請求庫,由于這個項目是2015年左右的,當時還是使用的HttpClient封裝。
android 6.0(API 23)中,Google已經移除了移除了Apache HttpClient相關的類
推薦使用HttpUrlConnection,如果要繼續使用需要Apache HttpClient,需要在eclipselibs里添加org.apache.http.legacy.jarandroid studio里在相應的module下的build.gradle中加入:

android {
useLibrary 'org.apache.http.legacy'
}

HttpClient添加cookie的簡單說明

  • 域名:參數中URL域名,一級域名不帶path。例如鏈接https://www.baidu.com/ 需要變換為 www.baidu.com,不能帶端口:8080)不帶Http(s)://也不待任何后面拼接的連接地址/path,否則無效。

  • 存儲方式:CookieStore為接口可自定義,http請求時會構建持續化永久存儲cookie相關信息,Cookie 一般是采用SharedPreferences持久化存儲、需要的時候讀取本地的SharedPreferences后遍歷放入ConcurrentHashMap,最終本質為httpContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
    其中ClientContext.COOKIE_STORE="http.cookie-store"

  • 存儲cookie一般都需要編碼、解碼、看是否過期等

使用

Application中的oncreate初始化時就設置cookiestore,如果有自定義可以自己添加后再設置持久性的存儲。

    //設置持久化cookieStore
        persistentCookieStore = new PersistentCookieStore(getApplicationContext());
        if (!URLEnvironmentConfig.isForPublish()) {
            persistentCookieStore.addCookie(persistentCookieStore.getCookie("gray_level", "fat", BuildConfig.BuildEnvironmentDomain.split("/")[0]));
        }
        asyncHttpClient.setCookieStore(persistentCookieStore);

   /**
     * 設置cookiestore的本質
     * Sets an optional CookieStore to use when making requests
     * @param cookieStore The CookieStore implementation to use, usually an instance of {@link PersistentCookieStore}
     */
    public void setCookieStore(CookieStore cookieStore) {
        httpContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
    }

image.png
package com.loopj.android.http;
import org.apache.http.client.CookieStore;
/*
    Android Asynchronous Http Client
    Copyright (c) 2011 James Smith <james@loopj.com>
    http://loopj.com

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

package com.loopj.android.http;

import java.util.concurrent.ConcurrentHashMap;

import org.apache.http.client.CookieStore;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.cookie.BasicClientCookie;


/**
 * A persistent cookie store which implements the Apache HttpClient
 * {@link CookieStore} interface. Cookies are stored and will persist on the
 * user's device between application sessions since they are serialized and
 * stored in {@link SharedPreferences}.
 * <p>
 * Instances of this class are designed to be used with
 * {@link AsyncHttpClient#setCookieStore}, but can also be used with a
 * regular old apache HttpClient/HttpContext if you prefer.
 */
public class PersistentCookieStore implements CookieStore {
    private static final String COOKIE_PREFS = "CookiePrefsFile";
    private static final String COOKIE_NAME_STORE = "names";
    private static final String COOKIE_NAME_PREFIX = "cookie_";

    private final ConcurrentHashMap<String, Cookie> cookies;
    private final SharedPreferences cookiePrefs;

    /**
     * Construct a persistent cookie store.
     */
    public PersistentCookieStore(Context context) {
        cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
        cookies = new ConcurrentHashMap<String, Cookie>();
        // Load any previously stored cookies into the store
        String storedCookieNames = cookiePrefs.getString(COOKIE_NAME_STORE, null);
        if (storedCookieNames != null) {
            String[] cookieNames = TextUtils.split(storedCookieNames, ",");
            for (String name : cookieNames) {
                String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + name, null);
                if (encodedCookie != null) {
                    Cookie decodedCookie = decodeCookie(encodedCookie);
                    if (decodedCookie != null) {
                        cookies.put(name, decodedCookie);
                    }
                }
            }

            // Clear out expired cookies
            clearExpired(new Date());
        }
    }

    @Override
    public void addCookie(Cookie cookie) {
        String name = cookie.getName() + cookie.getDomain();
        // Save cookie into local store, or remove if expired
        if (!cookie.isExpired(new Date())) {
            cookies.put(name, cookie);
        } else {
            cookies.remove(name);
        }

        // Save cookie into persistent store
        SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
        prefsWriter.putString(COOKIE_NAME_STORE, TextUtils.join(",", cookies.keySet()));
        prefsWriter.putString(COOKIE_NAME_PREFIX + name, encodeCookie(new SerializableCookie(cookie)));
        prefsWriter.commit();
    }


    @Override
    public void clear() {
        // Clear cookies from persistent store
        SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
        for (String name : cookies.keySet()) {
            prefsWriter.remove(COOKIE_NAME_PREFIX + name);
        }
        prefsWriter.remove(COOKIE_NAME_STORE);
        prefsWriter.commit();

        // Clear cookies from local store
        cookies.clear();
    }

    @Override
    public boolean clearExpired(Date date) {
        boolean clearedAny = false;
        SharedPreferences.Editor prefsWriter = cookiePrefs.edit();

        for (ConcurrentHashMap.Entry<String, Cookie> entry : cookies.entrySet()) {
            String name = entry.getKey();
            Cookie cookie = entry.getValue();
            if (cookie.isExpired(date)) {
                // Clear cookies from local store
                cookies.remove(name);

                // Clear cookies from persistent store
                prefsWriter.remove(COOKIE_NAME_PREFIX + name);

                // We've cleared at least one
                clearedAny = true;
            }
        }

        // Update names in persistent store
        if (clearedAny) {
            prefsWriter.putString(COOKIE_NAME_STORE, TextUtils.join(",", cookies.keySet()));
        }
        prefsWriter.commit();

        return clearedAny;
    }

    @Override
    public List<Cookie> getCookies() {
        return new ArrayList<Cookie>(cookies.values());
    }

    /**
     * 創建Cookie 比如灰度 gray_level=fat
     * @param name
     * @param value
     * @param domain
     * @return
     */
    public Cookie getCookie(String name, String value, String domain) {
//        BasicCookieStore cookieStore = new BasicCookieStore();
        BasicClientCookie cookie = new BasicClientCookie(name, value);
        cookie.setDomain(domain);
        cookie.setPath("/");
//        cookieStore.addCookie(cookie);
        return cookie;
    }

    //
    // Cookie serialization/deserialization
    //

    protected String encodeCookie(SerializableCookie cookie) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            ObjectOutputStream outputStream = new ObjectOutputStream(os);
            outputStream.writeObject(cookie);
        } catch (Exception e) {
            return null;
        }

        return byteArrayToHexString(os.toByteArray());
    }

    protected Cookie decodeCookie(String cookieStr) {
        byte[] bytes = hexStringToByteArray(cookieStr);
        ByteArrayInputStream is = new ByteArrayInputStream(bytes);
        Cookie cookie = null;
        try {
            ObjectInputStream ois = new ObjectInputStream(is);
            cookie = ((SerializableCookie) ois.readObject()).getCookie();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return cookie;
    }

    // Using some super basic byte array <-> hex conversions so we don't have
    // to rely on any large Base64 libraries. Can be overridden if you like!
    protected String byteArrayToHexString(byte[] b) {
        StringBuffer sb = new StringBuffer(b.length * 2);
        for (byte element : b) {
            int v = element & 0xff;
            if (v < 16) {
                sb.append('0');
            }
            sb.append(Integer.toHexString(v));
        }
        return sb.toString().toUpperCase();
    }

    protected byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }
}

Webview中的cookie設置簡單說明

  • 參數中URL域名。例如鏈接https://www.baidu.com/ 需要變換為 www.baidu.com,不能帶端口:8080)不帶Http(s)://也不待任何后面拼接的連接地址/path,否則無效。

  • 設置cookie先將本地存儲的cookie使用 for循環遍歷后setCookie(String url, String value) 。如果是要手動添加額外的cookie則使用分號;拼接的 value 值,注意value 的值是使用key=value; domain="域名" ;的完整形式。文檔提示 the cookie as a string, using the format of the 'Set-Cookie' HTTP response header,注意域名的正確性,否則會導致 Cookie不能完整設置或者無效。

  • CookieSyncManager 是個過時的類,Api21WebView 可以自動同步。 CookieSyncManager.getInstance().sync(); 被CookieManager.getInstance().flush()替代;其中Sync方法的本質調用的還是flush

  • 添加CookieloadUrl(url) 前一句調用進行 Cookie 同步操作。

  • cookieManager.setAcceptCookie(true);(自己測試結果不設置也生效)。

  • Cookie 同步方法要在WebViewsetting設置完之后調用,否則無效。(我測試的是oncreatesetContentView之后,可能是WebView為自定義的,所以相當于已經完成了setting的一系列設置)

  /**
     * webview中設置cookie
     * @param doMainHostNoProtocolURL 域名,不帶有http和別的相關的拼接字段
     */
    public static void addLoginCookie(String doMainHostNoProtocolURL) {
        //登錄成功后 重新設置webviewcookie信息 用來保持session一致...................start
        CookieSyncManager.createInstance(App.getInstance().getApplicationContext());
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.setAcceptCookie(true);
        //獲取本地存儲的cookie
        List<Cookie> cookies = App.getPersistentCookiesList();
        for (int i = 0; i < cookies.size(); i++) {
            Cookie cookie = cookies.get(i);
            String cookieString = cookie.getName() + "=" + cookie.getValue() + "; domain=" + cookie.getDomain();
            cookieManager.setCookie(doMainHostNoProtocolURL, cookieString);
        }
        
        //根據需求手動拼接添加cookie進去
        if (!URLEnvironmentConfig.isForPublish()) {
            String cookieString = "gray_level=fat" + "; domain=" + doMainHostNoProtocolURL;
            cookieManager.setCookie(doMainHostNoProtocolURL, cookieString);

        }
        CookieSyncManager.getInstance().sync();
        //..................................................................end
    }

根據項目中的需求比如按照以下方式設置

BuildConfig.BuildEnvironmentDomain是根據build編譯所需環境獲取相應的生產、測試、預發布域名。


    public static void addLoginCookie() {
        //登錄成功后 重新設置webviewcookie信息 用來保持session一致...................start
        CookieSyncManager.createInstance(App.getInstance().getApplicationContext());
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.setAcceptCookie(true);//不設置也生效
        //獲取本地存儲的cookie
        List<Cookie> cookies = App.getPersistentCookiesList();
        for (int i = 0; i < cookies.size(); i++) {
            Cookie cookie = cookies.get(i);
            String cookieString = cookie.getName() + "=" + cookie.getValue() + "; domain=" + cookie.getDomain();
            cookieManager.setCookie(BuildConfig.BuildEnvironmentDomain, cookieString);
        }
        if (!URLEnvironmentConfig.isForPublish()) {
            String cookieString = "gray_level=fat" + "; domain=" + BuildConfig.BuildEnvironmentDomain;
            cookieManager.setCookie(BuildConfig.BuildEnvironmentDomain, cookieString);

        }
          String AccessToken = "AccessToken="+LoginManager.getAccessToken()+"; domain="+URLEnvironmentConfig.getAppDomainNoProtocol();
            cookieManager.setCookie(URLEnvironmentConfig.getAppDomainNoProtocol(), AccessToken);

            String memberId = "memberId="+LoginManager.getMemberId()+"; domain="+URLEnvironmentConfig.getAppDomainNoProtocol();
            cookieManager.setCookie(URLEnvironmentConfig.getAppDomainNoProtocol(), memberId);

            String cliver = "cliver="+URLManager.getClientVersion()+"; domain="+URLEnvironmentConfig.getAppDomainNoProtocol();
            cookieManager.setCookie(URLEnvironmentConfig.getAppDomainNoProtocol(), cliver);

            String devid = "devid="+DeviceUtil.getDeviceID()+"; domain="+URLEnvironmentConfig.getAppDomainNoProtocol();
            cookieManager.setCookie(URLEnvironmentConfig.getAppDomainNoProtocol(), devid);


        CookieSyncManager.getInstance().sync();
        //..................................................................end

    }

域名獲取

/**
 * 獲取URL的域名
 */
private String getDomain(String url){
  url = url.replace("http://", "").replace("https://", "");
  if (url.contains("/")) {
    //url=url.split("/")[0];
    url = url.substring(0, url.indexOf('/'));

  }
  return url;
}

請求域名不同問題

本人未測試先記錄下:
兩個不同接口A、B,保持相同的domainHostpathname。只有cookiedomainpath與請求的URL匹配才會發送這個cookie
以上保持相同的則前者和后者不一致會被后者替換、

package android.webkit;

  /**
     * Sets a cookie for the given URL. Any existing cookie with the same host,
     * path and name will be replaced with the new cookie. The cookie being set
     * will be ignored if it is expired.
     *
     * @param url the URL for which the cookie is to be set
     * @param value the cookie as a string, using the format of the 'Set-Cookie'
     *              HTTP response header
     */
    public abstract void setCookie(String url, String value);

WebView中設置UA

原則是先獲取已有UA,再拼接新添加的部分,避免可能別的地方已經設置過了結果重新設置后覆蓋掉。

String original = webView.getSettings().getUserAgentString();// 獲取 WebView 的 UserAgent
original += " Version:" + versionName + ";";// 替換
webView.getSettings().setUserAgentString(original);// 設置新的 UserAgent

webview中添加請求頭header設置、

private Map<String,String>  extraHeaders = new HashMap<>();
extraHeaders.put("x-app-token", App.getToken());
extraHeaders.put("x-app-deviceId",  App.getDeviceId()));
if (mWebView != null) {
    mWebView.loadUrl(targetUrl, extraHeaders);
}

參考地址

在HttpClient上配置Cookie管理
Android 控件WebView設置Cookie

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。