How to use SSL and session cookies with Spring for Android

In this post I will be writing about setting up a secure connection with basic authentication and using session cookies with Spring for Android. For a basic how to start using Spring for Android see my earlier post titled “How to Create Restful Http for Android in 4 Easy Steps

For the current project I am working on we needed to create a secure connection with basic http authentication using the user’s username and password as params and capture / store an authentication cookie being returned by the server in the response headers.

The first thing that was addressed was creating the HttpBasicAuthentication headers. In order to do this, we needed to construct our request entity by using HttpAuthenticationHeaders. A basic example of a method that returns the authHeader might look like this:

	protected HttpHeaders getAuthenticatedRequestHeaders(String username, String password){
	
    // Set the BasicAuth needed for server
		HttpAuthentication authHeader = new HttpBasicAuthentication(username,password);
		HttpHeaders requestHeaders = new HttpHeaders();
		requestHeaders.setAuthorization(authHeader);
		// Set the Content-Type header
		requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
		// set the accept type to json
		requestHeaders.setAccept(Collections.singletonList(new MediaType("application","json")));
		requestHeaders.setAcceptEncoding(ContentCodingType.GZIP);
		
		return requestHeaders;
	}

Using this in the request entity was then as easy as creating a new request entity object using the constructor that takes the http headers as a param.

	//create the request entity
	HttpEntity<?> requestEntity = new HttpEntity<Object>(getAuthenticatedRequestHeaders("myUserName", "myPassword"));

For a POST request we could use the constructor that takes a body and headers as params.


//create the request entity
HttpEntity<?> requestEntity = new HttpEntity<Object>(body, getAuthenticatedRequestHeaders("myUserName", "myPassword"));

The next issue that needed to be addressed was retrieving and then setting the session cookie for an authenticated user. In this example the server was returning an authenticated User object with the session cookie in the response header. In the following code the response headers are inspected for the “Set-Cookie” header and that value is then saved to preferences or some other persistent storage for use with later authenticated requests to the server.

    
    ResponseEntity<UserContainer> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, UserContainer.class);
    UserContainer resultContainer = (UserContainer) response.getBody();    			
    HttpHeaders rHeaders = response.getHeaders();
    HttpHeaders rHeaders = response.getHeaders();
    List<String> val = rHeaders.get("Set-Cookie");
    if(null != val) {
       cookie = val.get(0);
       Editor ed = prefs.edit();
       String cookie = val.get(0);
       ed.putString(PREFS_KEY_USER_COOKIE, cookie);
       ed.commit();
     }

After saving the cookie to SharedPreferences, it could then be added to the headers of future requests by retrieving it and setting it in the request header. Here is an example of some code where the session cookie is retrieved and set in the request headers so an authenticated user’s details can be retrieved.

SharedPreferences mPrefs = getSharedPreferences(PREFS_PRIVATE_DATA, Context.MODE_PRIVATE);
String cookie = mPrefs.getString(PREFS_KEY_USER_COOKIE,"no data");
HttpHeaders requestHeaders = getAuthenticatedRequestHeaders("myUserName","myPassword");
//set the session cookie
requestHeaders.set("Cookie", cookie);
//create the request entity
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(getMessageConverters());
HttpClient httpClient = HttpUtils.getNewHttpClient();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
try {
  ResponseEntity<UserDetailContainer>response= restTemplate.exchange(url, HttpMethod.GET, requestEntity, UserDetailContainer.class);
  UserDetailContainer resultContainer = (UserDetailContainer) response.getBody();
  return resultContainer.getUserDetail();
// more detailed exception handling should be implemented here
}catch(Exception ex){
  ex.printStackTrace();
  return null;
}

So far we’ve seen how to implement HttpBasicAuthentication headers in Spring and how to retrieve and set a session cookie returned by the server in the response headers. The final piece of the puzzle has to do with getting an instance of HttpClient set up to handle SSL connections. There are two classes to implement here. I found this great post by Kapil Jain titled “Connect over secure rest web service for mobile application on android” in which he lays out 2 classes to implement that allow a secure connection on Android.

The first class is Easy SSL Socket Factory and has the following code:


import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.conn.ssl.SSLSocketFactory;

public class EasySSLSocketFactory extends SSLSocketFactory {
  SSLContext sslContext = SSLContext.getInstance("TLS");

  public EasySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
    super(truststore);
    TrustManager tm = new X509TrustManager() {
      public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { }

      public void checkServerTrusted(X509Certificate[] chain,  String authType) throws CertificateException {}

      public X509Certificate[] getAcceptedIssuers() {
		return null;
	    }
    };

    sslContext.init(null, new TrustManager[] { tm }, null);
  }

  @Override
  public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
    return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }
  @Override
  public Socket createSocket() throws IOException {
    return sslContext.getSocketFactory().createSocket();
  }
}

The second class is the HttpUtils class and consists of the following code:



import java.security.KeyStore;

import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;

public class HttpUtils {

	public static HttpClient getNewHttpClient() {
		try {
		    //here we can set a custom keystore if we need to
			KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
			trustStore.load(null, null);


			SSLSocketFactory sf = new EasySSLSocketFactory(trustStore);
			sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

			HttpParams params = new BasicHttpParams();
			
			HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
			HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

			SchemeRegistry registry = new SchemeRegistry();
			registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
			registry.register(new Scheme("https", sf, 443));

			ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

			return new DefaultHttpClient(ccm, params);
		} catch (Exception e) {
			return new DefaultHttpClient();
		}
	}

}

All that remains is to create a new RestTemplate object that will use the HttpClient provided by the HttpUtils class.

RestTemplate restTemplate = new RestTemplate();
HttpClient httpClient = HttpUtils.getNewHttpClient();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));

And that is how to implement secure connection using basic auth and session cookies with Spring for Android.

In the next post we will look at how to implement custom exception handling with Spring and Android.

One Response to “How to use SSL and session cookies with Spring for Android”