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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351