前言
服務端可能需要根據cookie
值判定是否有登錄、灰度環境、獲取相關有效信息等用于區分當前用戶。
HttpClient請求添加Cookie
現在大多數項目估計都是使用的okhttp
網絡請求庫,由于這個項目是2015年左右的,當時還是使用的HttpClient
封裝。
在android 6.0(API 23)
中,Google
已經移除了移除了Apache HttpClient
相關的類
推薦使用HttpUrlConnection
,如果要繼續使用需要Apache HttpClient
,需要在eclipse
下libs
里添加org.apache.http.legacy.jar
,android 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);
}
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
是個過時的類,Api21
中WebView
可以自動同步。CookieSyncManager.getInstance().sync()
; 被CookieManager.getInstance().flush()
替代;其中Sync
方法的本質調用的還是flush
添加
Cookie
在loadUrl(url)
前一句調用進行Cookie
同步操作。cookieManager.setAcceptCookie(true)
;(自己測試結果不設置也生效)。Cookie
同步方法要在WebView
的setting
設置完之后調用,否則無效。(我測試的是oncreate
中setContentView
之后,可能是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
,保持相同的domainHost
、path
、name
。只有cookie
的domain
和path
與請求的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);
}