前言
服务端可能需要根据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);
}