Android Httpclient Persistent Cookies

: Persistent cookies used in HttpClient and HttpGet access

The original question was: how do I prevent login each time after app start? Answer is of course to use cookies. So how do I store cookies so I can reuse after app restart?

The answer that works for me is very simple: just convert the cookiestore to a string, put it in the shared preferences before exiting the app.
After the next app start, so before the next login, just get the sting from the shared preferences, convert it back to a cookiestore. Using the cookiestore prevents me logging in again.

public void saveCookieStore( CookieStore cookieStore) {
StringBuilder cookies = new StringBuilder();
for (final Cookie cookie : cookieStore.getCookies()) {
cookies.append(cookie.getName());
cookies.append('=');
cookies.append(cookie.getValue());
cookies.append('=');
cookies.append(cookie.getDomain());
cookies.append(';');
}
SharedPreferences.Editor edit = sharedPref.edit();
edit.putString( MY_GC_COOKIESTORE, cookies.toString());
edit.commit();
}

// retrieve the saved cookiestore from the shared pref
public CookieStore restoreCookieStore() {
String savedCookieString = sharedPref.getString( MY_GC_COOKIESTORE, null);
if( savedCookieString == null || savedCookieString.isEmpty()) {
return null;
}
CookieStore cookieStore = new BasicCookieStore();
for ( String cookie : StringUtils.split( savedCookieString, ';')) {
String[] split = StringUtils.split( cookie, "=", 3);
if( split.length == 3) {
BasicClientCookie newCookie = new BasicClientCookie( split[0], split[1]);
newCookie.setDomain( split[2]);
cookieStore.addCookie( newCookie);
}
}
return cookieStore;
}

Android HttpClient persistent cookies

You can do what @Emmanuel suggested or you can pass the BasicHttpContext between the HttpClients you are creating.

Example Use of context and cookies, complete code here

    HttpClient httpclient = new DefaultHttpClient();

// Create a local instance of cookie store
CookieStore cookieStore = new BasicCookieStore();

// Create local HTTP context
HttpContext localContext = new BasicHttpContext();
// Bind custom cookie store to the local context
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);

HttpGet httpget = new HttpGet("http://www.google.com/", localContext);

Keep cookie session on android with httpclient

I made it to work after make my httpClient as Singleton. I don't know if this is the best solution to keep the session across the app, but it is working.

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());
}
}

Android: Persisting Server Session using cookie When Making HTTP calls

The "correct" way of doing this is to implement a CookieHandler: http://developer.android.com/reference/java/net/CookieHandler.html

The most basic way of doing this is to extend Application and put this in your applications onCreate():

CookieHandler.setDefault(new CookieManager());

PLEASE NOTE: This will only implement a DEFAULT CookieManger. The default CookieManger will manage cookies for all of your HTTP requests during a specific session of your application. However, it does not have any means of persisting cookies over subsequent uses of the application.

In order to do that, you'll need to write your own cookie manager by implementing CookieStore:
http://developer.android.com/reference/java/net/CookieStore.html

Here's an example of a CookieStore implementation i used in an app that is currently in the Google Play store:

package com.touchvision.util;

import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Log;

import com.touchvision.Config;

/*
* This is a custom cookie storage for the application. This
* will store all the cookies to the shared preferences so that it persists
* across application restarts.
*/
public class TvCookieStore implements CookieStore {

private static final String LOGTAG = "TV-TvCookieStore";

/*
* The memory storage of the cookies
*/
private Map<String, Map<String,String>> mapCookies = new HashMap<String, Map<String,String>>();
/*
* The instance of the shared preferences
*/
private final SharedPreferences sharedPrefs;

/*
* @see java.net.CookieStore#add(java.net.URI, java.net.HttpCookie)
*/
public void add(URI uri, HttpCookie cookie) {

String domain = cookie.getDomain();

// Log.i(LOGTAG, "adding ( " + domain +", " + cookie.toString() );

Map<String,String> cookies = mapCookies.get(domain);
if (cookies == null) {
cookies = new HashMap<String, String>();
mapCookies.put(domain, cookies);
}
cookies.put(cookie.getName(), cookie.getValue());

if (cookie.getName().startsWith("SPRING_SECURITY") && !cookie.getValue().equals("")){
// Log.i(LOGTAG, "Saving rememberMeCookie = " + cookie.getValue() );
// Update in Shared Preferences
Editor e = sharedPrefs.edit();
e.putString(Config.PREF_SPRING_SECURITY_COOKIE, cookie.toString());
e.commit(); // save changes
}

}

/*
* Constructor
*
* @param ctxContext the context of the Activity
*/
public TvCookieStore(Context ctxContext) {

// Log.i(LOGTAG, "constructor()");

sharedPrefs = ctxContext.getSharedPreferences(Config.SHARED_PREF_NAME, Context.MODE_PRIVATE);
}

/*
* @see java.net.CookieStore#get(java.net.URI)
*/
public List<HttpCookie> get(URI uri) {

List<HttpCookie> cookieList = new ArrayList<HttpCookie>();

String domain = uri.getHost();

// Log.i(LOGTAG, "getting ( " + domain +" )" );

Map<String,String> cookies = mapCookies.get(domain);
if (cookies == null) {
cookies = new HashMap<String, String>();
mapCookies.put(domain, cookies);
}

for (Map.Entry<String, String> entry : cookies.entrySet()) {
cookieList.add(new HttpCookie(entry.getKey(), entry.getValue()));
// Log.i(LOGTAG, "returning cookie: " + entry.getKey() + "="+ entry.getValue());
}
return cookieList;

}

/*
* @see java.net.CookieStore#removeAll()
*/
public boolean removeAll() {

// Log.i(LOGTAG, "removeAll()" );

mapCookies.clear();
return true;

}

/*
* @see java.net.CookieStore#getCookies()
*/
public List<HttpCookie> getCookies() {

Log.i(LOGTAG, "getCookies()" );

Set<String> mapKeys = mapCookies.keySet();

List<HttpCookie> result = new ArrayList<HttpCookie>();
for (String key : mapKeys) {
Map<String,String> cookies = mapCookies.get(key);
for (Map.Entry<String, String> entry : cookies.entrySet()) {
result.add(new HttpCookie(entry.getKey(), entry.getValue()));
Log.i(LOGTAG, "returning cookie: " + entry.getKey() + "="+ entry.getValue());
}
}

return result;

}

/*
* @see java.net.CookieStore#getURIs()
*/
public List<URI> getURIs() {

Log.i(LOGTAG, "getURIs()" );

Set<String> keys = mapCookies.keySet();
List<URI> uris = new ArrayList<URI>(keys.size());
for (String key : keys){
URI uri = null;
try {
uri = new URI(key);
} catch (URISyntaxException e) {
e.printStackTrace();
}
uris.add(uri);
}
return uris;

}

/*
* @see java.net.CookieStore#remove(java.net.URI, java.net.HttpCookie)
*/
public boolean remove(URI uri, HttpCookie cookie) {

String domain = cookie.getDomain();

Log.i(LOGTAG, "remove( " + domain +", " + cookie.toString() );

Map<String,String> lstCookies = mapCookies.get(domain);

if (lstCookies == null)
return false;

return lstCookies.remove(cookie.getName()) != null;

}

}

The above custom CookieStore uses SharedPreferences to persist cookies. You implement the above class the similar to how you would implement the default CookieManager in your application class, but the line would look like this:

CookieHandler.setDefault( new CookieManager( new TvCookieStore(this), CookiePolicy.ACCEPT_ALL));

As you can see, the only Cookie i really cared about persisting was the Spring Security Cookie (we were using Spring Framework on the server side). Your code will obviously be different to account for your specific needs.

Another quick note: I tried countless times to do what you're doing and handle the persistence of cookies within my http client class. It was nothing but headaches. Give this strategy a shot.

Android Asynchronous Http Client (loopj) and the Persistent Cookie Store

So here is what I have done until now. It works, but I am not sure if it is best practice.

1) The cookie store initialises from the sharedpreference. so just create a new one each time you need it. be sure to use the same context each time. I am using getApplicationContext()

2) and 4) The cookie store handels it all for you. Just create a new one with the same context as the one you created earlier. As long as you are consistent, the cookies will initialise properly.

3) the cookie store keeps the shared preferences and its local attributes in sync so just call (new PersistentCookieStore(getApplicationContext())).clear();

My Code

RestClient.java

public static void setCookieStore(PersistentCookieStore cookieStore) {
client.setCookieStore(cookieStore);
}

LoginActivity.java

RestClient.setCookieStore(new PersistentCookieStore(getApplicationContext()));

MainActivity.java

private void loginRouter() {
PersistentCookieStore mCookieStore = new PersistentCookieStore(
getApplicationContext());
List<Cookie> cookies = mCookieStore.getCookies();
for (Cookie c : cookies) {
if (c.getName().equals("session")) {
startActivity(new Intent(this, HomeActivity.class));
finish();
}
}
launchSplashPage();
}

LoopJ AndroidAsyncHttp and request cookies

Since you are creating your own PersistentCookieStore instance, Simply use the myCookieStore instance you created. Like @robotoaster says add it before get().

OR

Do this

HttpContext httpContext = httpClient.getHttpContext();
CookieStore cookieStore = (CookieStore) httpContext.getAttribute(ClientContext.COOKIE_STORE);

Then follow instructions at http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/client/CookieStore.html .

Source: Cookies in loopj for android
(Straight from loopj)



Related Topics



Leave a reply



Submit