How to Implement Cookie Handling on Android Using Okhttp

Automatic cookie handling with OkHttp 3

right now I'm playing with it.
try PersistentCookieStore, add gradle dependencies for JavaNetCookieJar:

compile "com.squareup.okhttp3:okhttp-urlconnection:3.0.0-RC1"

and init

    // init cookie manager
CookieHandler cookieHandler = new CookieManager(
new PersistentCookieStore(ctx), CookiePolicy.ACCEPT_ALL);
// init okhttp 3 logger
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
// init OkHttpClient
OkHttpClient httpClient = new OkHttpClient.Builder()
.cookieJar(new JavaNetCookieJar(cookieHandler))
.addInterceptor(logging)
.build();

`

Add cookie to client request OkHttp

There are 2 ways you can do this:

OkHttpClient client = new OkHttpClient().newBuilder()
.cookieJar(new CookieJar() {
@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
}

@Override
public List<Cookie> loadForRequest(HttpUrl url) {
Arrays.asList(createNonPersistentCookie());
}
})
.build();

// ...

public static Cookie createNonPersistentCookie() {
return new Cookie.Builder()
.domain("publicobject.com")
.path("/")
.name("cookie-name")
.value("cookie-value")
.httpOnly()
.secure()
.build();
}

or simply

OkHttpClient client = new OkHttpClient().newBuilder()
.addInterceptor(chain -> {
final Request original = chain.request();
final Request authorized = original.newBuilder()
.addHeader("Cookie", "cookie-name=cookie-value")
.build();
return chain.proceed(authorized);
})
.build();

I have a feeling that the second suggestion is what you need.

You can find here a working example.

Persistent Cookie store using okhttp 2 on Android

Putting the code into a custom Application's onCreate function solves the issue. It works now.

public class MyApplication extends Application {
public void onCreate() {
super.onCreate();

// enable cookies
java.net.CookieManager cookieManager = new java.net.CookieManager();
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(cookieManager);
}
}

I managed to get persistent cookies with okhttp with the following CookieStore, which is partly copied from the one of android-async-http. It works with API lvl 9 and maybe fewer.

import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import android.util.Log;

import java.io.*;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
* A persistent cookie store which implements the Apache HttpClient CookieStore interface.
* Cookies are stored and will persist on the user's device between application sessions since they
* are serialized and stored in SharedPreferences. Instances of this class are
* designed to be used with 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 LOG_TAG = "PersistentCookieStore";
private static final String COOKIE_PREFS = "CookiePrefsFile";
private static final String COOKIE_NAME_PREFIX = "cookie_";

private final HashMap<String, ConcurrentHashMap<String, HttpCookie>> cookies;
private final SharedPreferences cookiePrefs;

/**
* Construct a persistent cookie store.
*
* @param context Context to attach cookie store to
*/
public PersistentCookieStore(Context context) {
cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
cookies = new HashMap<String, ConcurrentHashMap<String, HttpCookie>>();

// Load any previously stored cookies into the store
Map<String, ?> prefsMap = cookiePrefs.getAll();
for(Map.Entry<String, ?> entry : prefsMap.entrySet()) {
if (((String)entry.getValue()) != null && !((String)entry.getValue()).startsWith(COOKIE_NAME_PREFIX)) {
String[] cookieNames = TextUtils.split((String)entry.getValue(), ",");
for (String name : cookieNames) {
String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + name, null);
if (encodedCookie != null) {
HttpCookie decodedCookie = decodeCookie(encodedCookie);
if (decodedCookie != null) {
if(!cookies.containsKey(entry.getKey()))
cookies.put(entry.getKey(), new ConcurrentHashMap<String, HttpCookie>());
cookies.get(entry.getKey()).put(name, decodedCookie);
}
}
}

}
}
}

@Override
public void add(URI uri, HttpCookie cookie) {
String name = getCookieToken(uri, cookie);

// Save cookie into local store, or remove if expired
if (!cookie.hasExpired()) {
if(!cookies.containsKey(uri.getHost()))
cookies.put(uri.getHost(), new ConcurrentHashMap<String, HttpCookie>());
cookies.get(uri.getHost()).put(name, cookie);
} else {
if(cookies.containsKey(uri.toString()))
cookies.get(uri.getHost()).remove(name);
}

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

protected String getCookieToken(URI uri, HttpCookie cookie) {
return cookie.getName() + cookie.getDomain();
}

@Override
public List<HttpCookie> get(URI uri) {
ArrayList<HttpCookie> ret = new ArrayList<HttpCookie>();
if(cookies.containsKey(uri.getHost()))
ret.addAll(cookies.get(uri.getHost()).values());
return ret;
}

@Override
public boolean removeAll() {
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.clear();
prefsWriter.commit();
cookies.clear();
return true;
}

@Override
public boolean remove(URI uri, HttpCookie cookie) {
String name = getCookieToken(uri, cookie);

if(cookies.containsKey(uri.getHost()) && cookies.get(uri.getHost()).containsKey(name)) {
cookies.get(uri.getHost()).remove(name);

SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
if(cookiePrefs.contains(COOKIE_NAME_PREFIX + name)) {
prefsWriter.remove(COOKIE_NAME_PREFIX + name);
}
prefsWriter.putString(uri.getHost(), TextUtils.join(",", cookies.get(uri.getHost()).keySet()));
prefsWriter.commit();

return true;
} else {
return false;
}
}

@Override
public List<HttpCookie> getCookies() {
ArrayList<HttpCookie> ret = new ArrayList<HttpCookie>();
for (String key : cookies.keySet())
ret.addAll(cookies.get(key).values());

return ret;
}

@Override
public List<URI> getURIs() {
ArrayList<URI> ret = new ArrayList<URI>();
for (String key : cookies.keySet())
try {
ret.add(new URI(key));
} catch (URISyntaxException e) {
e.printStackTrace();
}

return ret;
}

/**
* Serializes Cookie object into String
*
* @param cookie cookie to be encoded, can be null
* @return cookie encoded as String
*/
protected String encodeCookie(SerializableHttpCookie cookie) {
if (cookie == null)
return null;
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(cookie);
} catch (IOException e) {
Log.d(LOG_TAG, "IOException in encodeCookie", e);
return null;
}

return byteArrayToHexString(os.toByteArray());
}

/**
* Returns cookie decoded from cookie string
*
* @param cookieString string of cookie as returned from http request
* @return decoded cookie or null if exception occured
*/
protected HttpCookie decodeCookie(String cookieString) {
byte[] bytes = hexStringToByteArray(cookieString);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
HttpCookie cookie = null;
try {
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
cookie = ((SerializableHttpCookie) objectInputStream.readObject()).getCookie();
} catch (IOException e) {
Log.d(LOG_TAG, "IOException in decodeCookie", e);
} catch (ClassNotFoundException e) {
Log.d(LOG_TAG, "ClassNotFoundException in decodeCookie", e);
}

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!
*
* @param bytes byte array to be converted
* @return string containing hex values
*/
protected String byteArrayToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte element : bytes) {
int v = element & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString().toUpperCase(Locale.US);
}

/**
* Converts hex values from strings to byte arra
*
* @param hexString string of hex-encoded values
* @return decoded byte array
*/
protected byte[] hexStringToByteArray(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16));
}
return data;
}
}

The SerializableHttpCookie.java:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.HttpCookie;

public class SerializableHttpCookie implements Serializable {
private static final long serialVersionUID = 6374381323722046732L;

private transient final HttpCookie cookie;
private transient HttpCookie clientCookie;

public SerializableHttpCookie(HttpCookie cookie) {
this.cookie = cookie;
}

public HttpCookie getCookie() {
HttpCookie bestCookie = cookie;
if (clientCookie != null) {
bestCookie = clientCookie;
}
return bestCookie;
}

private void writeObject(ObjectOutputStream out) throws IOException {
out.writeObject(cookie.getName());
out.writeObject(cookie.getValue());
out.writeObject(cookie.getComment());
out.writeObject(cookie.getCommentURL());
out.writeObject(cookie.getDomain());
out.writeLong(cookie.getMaxAge());
out.writeObject(cookie.getPath());
out.writeObject(cookie.getPortlist());
out.writeInt(cookie.getVersion());
out.writeBoolean(cookie.getSecure());
out.writeBoolean(cookie.getDiscard());
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
String name = (String) in.readObject();
String value = (String) in.readObject();
clientCookie = new HttpCookie(name, value);
clientCookie.setComment((String) in.readObject());
clientCookie.setCommentURL((String) in.readObject());
clientCookie.setDomain((String) in.readObject());
clientCookie.setMaxAge(in.readLong());
clientCookie.setPath((String) in.readObject());
clientCookie.setPortlist((String) in.readObject());
clientCookie.setVersion(in.readInt());
clientCookie.setSecure(in.readBoolean());
clientCookie.setDiscard(in.readBoolean());
}
}

Using CookieHandler with OkHttp and Retrofit 2

I think you set up everything correctly.
There are some issues with CookieManager in Android.
I spent a lot of time trying to make it works. As result I implemented small custom implementation of cookies managment via interceptors.

You can read more about it:

Do cookies in OkHttp

Do cookies in OkHttp 2

AOSP issue 75182

Also you could find many related post on SO

OkHTTPClient pass cookies to Webview

With OkHttp 3.0, you can use a method similar to sharing with HttpURLConnection by making a WebkitCookieManagerProxy that uses the webkit cookie store. Adapted from Pass cookies from HttpURLConnection (java.net.CookieManager) to WebView (android.webkit.CookieManager) .

public class WebkitCookieManagerProxy extends CookieManager implements CookieJar {
private android.webkit.CookieManager webkitCookieManager;

private static final String TAG = WebkitCookieManagerProxy.class.getSimpleName();

public WebkitCookieManagerProxy() {
this(null, null);
}

WebkitCookieManagerProxy(CookieStore store, CookiePolicy cookiePolicy) {
super(null, cookiePolicy);
this.webkitCookieManager = android.webkit.CookieManager.getInstance();
}

@Override
public void put(URI uri, Map<String, List<String>> responseHeaders)
throws IOException {
// make sure our args are valid
if ((uri == null) || (responseHeaders == null))
return;

// save our url once
String url = uri.toString();

// go over the headers
for (String headerKey : responseHeaders.keySet()) {
// ignore headers which aren't cookie related
if ((headerKey == null)
|| !(headerKey.equalsIgnoreCase("Set-Cookie2") || headerKey
.equalsIgnoreCase("Set-Cookie")))
continue;

// process each of the headers
for (String headerValue : responseHeaders.get(headerKey)) {
webkitCookieManager.setCookie(url, headerValue);
}
}
}

@Override
public Map<String, List<String>> get(URI uri,
Map<String, List<String>> requestHeaders) throws IOException {
// make sure our args are valid
if ((uri == null) || (requestHeaders == null))
throw new IllegalArgumentException("Argument is null");

// save our url once
String url = uri.toString();

// prepare our response
Map<String, List<String>> res = new java.util.HashMap<String, List<String>>();

// get the cookie
String cookie = webkitCookieManager.getCookie(url);

// return it
if (cookie != null) {
res.put("Cookie", Arrays.asList(cookie));
}

return res;
}

@Override
public CookieStore getCookieStore() {
// we don't want anyone to work with this cookie store directly
throw new UnsupportedOperationException();
}

@Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
HashMap<String, List<String>> generatedResponseHeaders = new HashMap<>();
ArrayList<String> cookiesList = new ArrayList<>();
for(Cookie c: cookies) {
// toString correctly generates a normal cookie string
cookiesList.add(c.toString());
}

generatedResponseHeaders.put("Set-Cookie", cookiesList);
try {
put(url.uri(), generatedResponseHeaders);
} catch (IOException e) {
Log.e(TAG, "Error adding cookies through okhttp", e);
}
}

@Override
public List<Cookie> loadForRequest(HttpUrl url) {
ArrayList<Cookie> cookieArrayList = new ArrayList<>();
try {
Map<String, List<String>> cookieList = get(url.uri(), new HashMap<String, List<String>>());
// Format here looks like: "Cookie":["cookie1=val1;cookie2=val2;"]
for (List<String> ls : cookieList.values()) {
for (String s: ls) {
String[] cookies = s.split(";");
for (String cookie : cookies) {
Cookie c = Cookie.parse(url, cookie);
cookieArrayList.add(c);
}
}
}
} catch (IOException e) {
Log.e(TAG, "error making cookie!", e);
}
return cookieArrayList;
}

}

Then add an instance of the proxy as your cookieJar when building the OkHttpClient.

client = new OkHttpClient.Builder().cookieJar(proxy).build();


Related Topics



Leave a reply



Submit