Securing REST WebServices with Spring Security and Custom HTTP Request Factories

Jorge Simão

Abstract

I show how to implement secure REST WebServices using HTTP authentication and Spring Security framework. I review the server side configuration for Spring Security to setup Basic and Digest HTTP authentication. For the client side the Spring Framework class RestTemplate is tailored with a custom ClientHttpRequestFactory so that the HTTP headers are set with the required HTTP authentication information.

Keywords: REST WebServices, HTTP Basic and Digest Authentication, Spring Security, RestTemplate

Introduction

Rest web-services (REST-WS) provide an effective and portable away to implement distributed services based on HTTP 1.1 protocol. HTTP specifies two standard mechanisms for authentication: Basic and Digest. These two elements suggest that Rest web services can naturally be secured with the authentication mechanisms of HTTP.

Spring security is a framework that can be used to secure Java based services in a declarative away, including those based on HTTP and Servlet. Spring security has many built-in authentication mechanisms, including Basic and Digest HTTP authentication. Setting up these authentication mechanisms in the server side is a simple matter of configuring Spring Security appropriately.

For the client side, assuming a Java implementation, the Spring Framework class RestTemplate provides a intuitive API to invoke REST-WS. in client application. There is no out-of-the-box implementation of RestTemplate implementing HTTP authentication. However, RestTemplate is highly customizable, and allows setting of a custom factory bean to create HTTP connections. This strategy bean is defined by type ClientHttpRequestFactory, and application can provide implementation suiting their needs. By providing a custom ClientHttpRequestFactory that sets HTTP headers appropriately, is possible to implement secure REST-WS based on HTTP authentication.

In this article, I show how to use the above approach to implement secure REST-WS. I start with Basic HTTP authentication, and later repeat the approach for Digest HTTP authentication. For the Basic authentication, I use the Java HttpConnection as a low-level factory for HTTP connections. For Digest authentication I use the Apache HttpClient library to simplify development.

Securing REST Web Services: Basic Authentication

Setting up a secure REST-WS with Spring Framework and Spring Security, involves three steps:

  • Implement the resource(s) to be secured, such as REST controllers implemented with Spring MVC;
  • Configure Spring Security with the desired authentication and access control rule for the secured resources;
  • Install Spring Security filter-chain as part of the application deployment descriptor (/WEB-INF/web.xml).

Implementing a Secure REST Web Service Controller

Server-side security involves performing user authentication and checking for allowed access to secured resources. For illustration purpose, I define a simple resource to be secured -- a handler method of a controller that implements a REST-WS.

Below, I show a Spring MVC enabled web controller (SecuredRestWSController) for a REST-WS with a single method that to be secured -- the secured() method. The method is mapped to URL /secured and HTTP method GET. The return value of the method is a String value that is to be marshalled in the HTTP response message body. This is specified with annotation @ResponseBody.

@Controller
public class SecuredRestWSController {
  ...
	@RequestMapping(value = "/secured", method = RequestMethod.GET)
	public @ResponseBody String secured() {
		return "SECURED: " + new Date();
	}
}

Spring Security Configuration

Spring security is most commonly configured in a Spring beans XML configuration file, importing the Spring security XML namespace. Below, I review the typical top-level structure of a XML configuration file using Spring Security namespace set as the default namespace.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns="http://www.springframework.org/schema/security"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="
	http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
	http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">	
  ...	
</beans:beans>

With the security namespace set as default, security configuration elements can be conveniently used without an XML namespace prefix. The Spring beans namespace is also imported to define the top-level element <beans:beans>. The schema location for these two namespaces is specified to enable tool-driven XML validation. Application security bean files should be included from the file(s) for the main application context, for the Spring MVC application context.

Below, I show how to setup the Spring Security configuration to secure the SecuredRestWSController web-service with Basic HTTP authentication. The element <http> is used to encapsulate all HTTP specific configuration.

<http>
  <http-basic />
  <intercept-url pattern="/secured**" access="ROLE_ADMIN" />
</http>

Most of the beans required to setup HTTP Basic authentication are automatically created and configured by the handler of the element <http-basic>. The element intercept-url is used to define a control access rule for the URL pattern /secured**, which conveniently matches the REST-WS web-service resource that we want to secure.

The other beans required to setup the authentication configuration are the repositories with user credentials (user-name and passwords), and their and their authorities (roles). This is done with element <authentication-manager> that is set with one (or more) authentication providers. Below, I use a simple in-memory list of user credentials specified with <user-service>. Other authentication providers could used.

<authentication-manager>
  <authentication-provider>
    <user-service>
      <user name="admin" password="admin" authorities="ROLE_USER, ROLE_ADMIN" />
      <user name="jsimao" password="jsimao" authorities="ROLE_USER, ROLE_ADMIN" />
      <user name="guest" password="guest" authorities="ROLE_GUEST" />
    </user-service>
  </authentication-provider>
</authentication-manager>

Configuring the Security Filter Chain

The final step to setup Spring Security is to setup the filter chain that intercepts HTTP requests before they reach the corresponding Servlets and application components -- such as the Spring MVC DispatcherServlet and the managed application web-controllers. This is done in the web application descriptor file located in /WEB-INF/web.xml, by configuring a filter of type DelegatingFilterProxy with name springSecurityFilterChain. The filter mapping is set to match (at least) the set of URLs we want to secure.

<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/secured</url-pattern>
</filter-mapping>

Building a Client for Secured Rest Web Services

This section shows how to implementing a Java client of secured REST-WS. The Spring Framework class RestTemplate is configured appropriately to support HTTP Basic authentication. The solution is specific to Java clients using Spring Framework, but porting to other client environments is also feasible.

Spring RestTemplate API provides methods that roughly correspond to the HTTP methods sent in a request. For example, to obtain a remote resource using an HTTP GET request we use RestTemplate.getForObject(). RestTemplate automatically perform marsalling and unmarshalling of Java objects and HTTP responses.

Since Spring Security was configured in your sample REST-WS to require HTTP Basic authentication, we need to tailor the behavior of RestTemplate so that the appropriate HTTP authentication headers are set. Fortunately, RestTemplate has extension point that allow for this. The strategy interface ClientHttpRequestFactory is used by RestTemplate to create the actual connection to the HTTP server. Two implementations are provided out-of-the-box: one implementation that wraps the java.net.HttpConnection class, this is SimpleClientHttpRequestFactory; and another that wraps the third-party open-source Apache HttpClient class. Because BASIC authentication is simple enough, I will start by using Java java.net.HttpConnection and SimpleClientHttpRequestFactory.

Invoking the Secure REST Web-Service

Below, I show the code for a simple REST-WS client that invokes a secured method. This is done by class RestClient. A Spring RestTemplate is defined as a data-field for the RestClient class, configured in the constructor. The configuration is done by setting a custom ClientHttpRequestFactory of type BasicSecureSimpleClientHttpRequestFactory though a call to the property setter method RestTemplate.setRequestFactory.

package org.helloapp.restsecurity.client;

import org.springframework.web.client.RestTemplate;

public class RestClient {
	RestTemplate restTemplate = new RestTemplate();
	static final String URL = "http://localhost:8081/rest-security/secured";
	static final String username = "admin";
	static final String password = "admin";	

	public RestClient() {
		BasicSecureSimpleClientHttpRequestFactory requestFactory = new BasicSecureSimpleClientHttpRequestFactory();
		requestFactory.setCredentialsProvider(new SimpleCrendentialsProvider(new Credentials(username, password)));
		restTemplate.setRequestFactory(requestFactory);
	}
	
	public void exec() {
		String response = restTemplate.getForObject(URL, String.class);
		System.out.println("Response: " + response);		
	}
	
	static public void main(String[] args) {
		RestClient client = new RestClient();
		client.exec();
	}
}

The custom class BasicSecureSimpleClientHttpRequestFactory is an extension to the default implementation used by RestTemplate. Its specific feature is to support Basic HTTP authentication. The instance of the custom class is further configured by setting a CredentialsProvider which is the object responsible to provide the user credentials information on request -- namely, the username and password.

Method RestClient.exec() invokes the REST web-service using the RestTemplate configured with BasicSecureSimpleClientHttpRequestFactory. Method RestTemplate.getForObject() invokes the service URL to get the response value sent by the server method SecuredRestWSController.secured().

Method RestClient.main() is used to run the client as standalone Java application.

Implementing Secure ClientHttpRequestFactory

The next step in our solution is to implement BasicSecureSimpleClientHttpRequestFactory. For this purpose, I define an abstract support class SecureSimpleClientHttpRequestFactory, that encapsulates much of the logic of implementing a secure REST-WS based java.net.HttpConnection. In particular, it allows the details of concrete security protocols to be "pluged-in" by implementing the abstract method prepareSecureConnection(). Although it is be used here to implement HTTP Basic authentication, it can also be extended to support other protocols such as HTTP Digest authentication.

Below, I show the code for SecureSimpleClientHttpRequestFactory:

package org.helloapp.restsecurity.client;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;

import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.SimpleClientHttpRequestFactory;


abstract public class SecureSimpleClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
	protected CredentialsProvider credentialsProvider;
	
	public SecureSimpleClientHttpRequestFactory() {
	}

	public CredentialsProvider getCredentialsProvider() {
		return credentialsProvider;
	}

	public void setCredentialsProvider(CredentialsProvider credentialsProvider) {
		this.credentialsProvider = credentialsProvider;
	}

	@Override
	public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
		HttpURLConnection connection = openConnection(uri.toURL(), null);
		prepareConnection(connection, httpMethod.name());
		prepareSecureConnection(connection);
		return new SecureSimpleClientHttpRequest(connection);
	}
	
	abstract protected void prepareSecureConnection(HttpURLConnection connection);

}

Method SimpleClientHttpRequestFactory.createRequest() return an instance of a ClientHttpRequest. This is the abstraction used by RestTemplate to create HTTP request messages (with headers and body). The implementation of ClientHttpRequest used by the default is SimpleClientHttpRequestFactory, but because it is defined with only a package visibility constructor it can not be used directly. In stead, I define a custom class SecureSimpleClientHttpRequestFactory that extends the support abstract class AbstractClientHttpRequest shipped with Spring Framework.

Method prepareSecureConnection() is called by createRequest() to setup the HttpURLConnection that is going to be used to perform the request. Since this is protocol specific, the method is defined as abstract and should be implemented by derived classes.

SimpleClientHttpRequestFactory has a property for a CredentialsProvider. This is object that can be used by derived classes to get user credentials information. The specification for this interface is show below:

public interface CredentialsProvider {
	Credentials getCredentials(String realm);
}

Credentials is simple data (transport) object that carries the user name and password:

public class Credentials {
  protected String userName;
  protected String password;

  //constructors
  
  //setters and getters for properties userName and password.
}

Implementing a Basic HTTP Secure RequestFactory

The final step in our client implementation is to extend SecureSimpleClientHttpRequestFactory and specialized it for HTTP BASIC authentication. This involves defining method prepareSecureConnection() to setup the HTTP header Authorization with the user credentials. Below, I show the implementation:

public class BasicSecureSimpleClientHttpRequestFactory extends SecureSimpleClientHttpRequestFactory {

	public BasicSecureSimpleClientHttpRequestFactory() {
	}

	protected void prepareSecureConnection(HttpURLConnection connection) {
		if (credentialsProvider==null) {
			return;
		}
		Credentials credentials = credentialsProvider.getCredentials(null);
	    String token = credentials.getUserName() + ":" + credentials.getUserName();
		BASE64Encoder enc = new sun.misc.BASE64Encoder();
	    String encodedAuthorization = enc.encode(token.getBytes());
	    connection.setRequestProperty("Authorization", "Basic " + encodedAuthorization);		
	}	
}

The HTTP Basic authentication protocol requires that the client send a HTTP request header named Authorization with the following form:

Authorization: Basic BASE64Encoded[userName ':' password]

The string Basic is used to specify that the provided credentials follow the HTTP Basic format. This is a BASE64 character encoded string with the name of the user and password concatenated with the double-column character ':'. Since custom coding the details of BASE64 encoding can be lead to implementation errors and is extra work, I use an existing implementation for the encoding. Namely, the class sun.misc.BASE64Encoder that is packaged with the Java SDK.

The server extracts the user credentials information by applying the reverse decoding operation. Since the decoding operation can be done without any sort of secret parameter, it can potentially be done by eavesdropper that intercept client messages. For all intends and purposes, it is as if the credentials are sent has plain text through the wire. This means that Basic HTTP authentication provides limited security again this kinds of attacks.

There are two turn around for HTTP limited security against eavesdropping: either drop the HTTP Basic authentication and use a more secure solution such as HTTP Digest authentication; or, alternatively, use a secure HTTP(/TCP/IP) channel between the client and the server. Channel security is usually implemented using an HTTPS connection that add an SSL layer on top of TCP/IP, to build the protocol stack: HTTPS = HTTP/SSL/TCP/IP. A modern variant is of SSL is TLS.

Note that if no CredentialsProvider is configured, then authentication headers can not be set. This is likely to cause the server to deny access to the resource and have an exception eventually throw by RestTemplate. An alternative approach would be to have the prepareSecureConnection() method throw a RuntimeException, so that no request is sent to the server in the first place.

Basic HTTP Client Authenthication with Apache Commons HttpClient

In the approach described above, I implemented a ClientHttpRequestFactory for Basic HTTP authentication that "manually" sets the HTTP Authenticate header on Java SDK HttpURLConnection. An alternative, and very popular, abstraction for HTTP communication in the client side is the Apache Commons HttpClient. Apache HttpClient has the advantage over Java HttpURLConnection that it has built-in support for different authentication protocols, including Basic and Digest HTTP authentication. This means that HttpClient can be used to implementing Java clients for secure REST-WS without having to deal directly with HTTP headers for authentication. Spring Framework also comes with an implementation of a ClientHttpRequestFactory that uses the Apache HttpClient -- this is the class CommonsClientHttpRequestFactory. Thus, HttpClient is an additional natural choice for implementing secure REST-WS with Spring Framework.

Below, I show the code for a custom ClientHttpRequestFactory that extends Spring CommonsClientHttpRequestFactory to work with HTTP authentication -- the class SecureCommonsClientHttpRequestFactory.

package org.helloapp.restsecurity.client;

import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.springframework.http.client.CommonsClientHttpRequestFactory;

public class SecureCommonsClientHttpRequestFactory extends CommonsClientHttpRequestFactory {
	protected CredentialsProvider credentialsProvider;
	
	public SecureCommonsClientHttpRequestFactory() {
	}

	public CredentialsProvider getCredentialsProvider() {
		return credentialsProvider;
	}

	public void setCredentialsProvider(CredentialsProvider credentialsProvider) {
		this.credentialsProvider = credentialsProvider;
		Credentials cred = credentialsProvider.getCredentials(null);
		UsernamePasswordCredentials defaultcreds = new UsernamePasswordCredentials(cred.getUserName(), cred.getPassword());
		getHttpClient().getState().setCredentials(AuthScope.ANY, defaultcreds);
	}
}

The key aspect of the implementation of Spring CommonsClientHttpRequestFactory is that an instance of HttpClient instance created and made available as a property. In the custom derived class above the HttpClient is configured to work with HTTP authentication by setting the user credential with method HttpClient.getState().setCredentials(). Because, in the test client code only one server (and realm) is considered, I assumed for simplification that the provided credentials are correct in all contexts -- rather than use a different username or password for each host and realm. This authentication scope information is specified to HttpClient with parameter value AuthScope.ANY.

Below, I show the HTTP request message sent by the REST client and the corresponding response sent by Spring MVC and Spring Security enabled REST-WS (captured and proxied with Eclipse TCP/IP Monitor tool):

Client HTTP Request Message:

GET /rest-security/secured HTTP/1.1
Accept: text/plain, */*
User-Agent: Jakarta Commons-HttpClient/3.1
Authorization: Basic YWRtaW46YWRtaW4=
Host: localhost:8080
Cookie: $Version=0; JSESSIONID=434A851852D7146B3C1608F6E38EB147; $Path=/rest-security

Server HTTP Response Message:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=9B3F0F0F021EF330E35C6A3A902C8BBD; Path=/rest-security; HttpOnly
Content-Type: text/plain
Content-Length: 38
Date: Wed, 28 Sep 2011 16:18:16 GMT

SECURED: Wed Sep 28 16:18:16 WEST 2011

Note that the client is sending the Authorization header already in the first (and only) request message. This prevents the server to deny the first request by lack of Authorization header, and for the client to have to send two requests.

Securing REST Web Services: Digest Authentication

For communication channels that transport highly sensitive information, HTTPS and SSL are a good solution to implement secure HTTP channels and complement HTTP Basic authentication. On the other hand, if not all the information to move around is sensible then encrypting data with HTTPS/SSL is an overkill. This is because encrypting large amounts of data is costly to the client, and specially, to the server that may need to serve large number of concurrent clients. If the only requirement for the HTTP channel is to protected the user password from easy disclosure, HTTP Digest authentication is a more recommended option.

In Digest authentication user information, such as name and password, are never sent has plain text on the wire. Rather, both client and server use the message digest algorithm MD5 to "scramble" the bits of security sensitive information. Because MD5 digests can not be reversed except by brute force analysis, there is limited possibilities of disclosure of sensitive information. Digest authentication also requires that a server provides a limited lifetime nonce (semi-random sequence of bits) to the client, and that the client includes this nonce in the computations for digests of security token. This further reduces the changes for attackers to replay security tokens previous sent on the wire.

Digest authentication piggybacks on HTTP protocol the same way as Basic authentication. Namely, clients needs to set the Authorization HTTP header with the appropriate security token. A key difference is, of course, that security token are encrypted with MD5. Another important difference, is that the first request sent by a client is always rejected because creating a valid Authorization needs a server provided nonce.

Additional Secured Resource

Because the implementation of secured service is independent of the security mechanisms used, we can often upgrade authentication mechanisms without any change on the service. For example, we can upgrade a service initially secured with Basic authentication to Digest authentication. On the other hand, is possible to have different services secured with different mechanisms.

For the sample REST-WS used in this article, I would like to keep the SecuredRestWSController.secure() method secured with Basic authentication. So I will define an additional method to be secured with Digest authentication. Below, I show the definition of this additional REST-WS method SecuredRestWSController.xsecured() accessible from URL /xsecured.

@Controller
public class SecuredRestWSController {
	...
	@RequestMapping(value = "/xsecured", method = RequestMethod.GET)
	public @ResponseBody String xsecured() {
		return "XSECURED: " + new Date();
	}
}

Configuring Spring Security for Digest Authentication

As hinted above, Spring Security uses a filter chain that is setup with a DelegatingFilterProxy configured in the application descriptor file web.xml. Internally, this works by having the DelegatingFilterProxy creating a Spring bean with type FilterChainProxy and with the same name as the filter name used for the DelegatingFilterProxy.

DelegatingFilterProxy delegates the actual work of filtering incoming HTTP request to an instance of FilterChainProxy. FilterChainProxy in turn takes the responsability of creating the individual filters and assemble them in the right order. Performing user authentication and authorizing access to resources is the outcome of the combined work of this individual filters.

Unless one explicitly configures a FilterChainProxy bean, the filter name for the DelegatingFilterProxy should be springSecurityFilterChain. This is because the Spring Security namespace configuration creates and configures a instance of type FilterChainProxy automatically with this bean name. And is the bean with this name that is lookedup in the root application context by DelegatingFilterProxy.

For security configurations with extensive XML namespace support, explicitly defining a FilterChainProxy Spring bean is usually not necessary. However, doing that allows additional or alternative filters to be configured as Spring bean files. The need for a custom chain exist because configuring HTTP Digest authentication requires configuring specialized filter(s) and beans. Unfortunately, Spring Security XML namespace support does not cover all the features of Spring Security. For Basic HTTP authentication the element <http-basic> sets up all the required infrastructure, but for Digest authentication there is no corresponding <http-digest>.

Below, we show an example of an explicit configuration of a FilterChainProxy to setup basic, digest, and form based authentication. The element <filter-chain> is used as a convenient way to specify a concrete filter chain for a particular URL pattern.

<beans:bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
  <beans:constructor-arg>
    <beans:list>
      <filter-chain pattern="/secured/**" filters="
           securityContextPersistenceFilterWithASCFalse,
           basicAuthenticationFilter,
           exceptionTranslationFilter,
           filterSecurityInterceptor" />
      <filter-chain pattern="/xsecured/" filters="
           securityContextPersistenceFilterWithASCFalse,
           basicAuthenticationFilter,
           exceptionTranslationFilter,
           filterSecurityInterceptor" />
      <filter-chain pattern="/**" filters="
           securityContextPersistenceFilterWithASCTrue,
           formLoginFilter,
           exceptionTranslationFilter,
           filterSecurityInterceptor" />
    </beans:list>
  </beans:constructor-arg>
</beans:bean>

The above configuration of FilterChainProxy is a bit verbose, because we are completely redefining the chain of filters to use. And since different URL prefixes use different authentication mechanisms, we need to setup a different chain for each URL pattern. (You can check the Spring Security reference documentation for a detailed explanation of the purpose and configuration of each of these filters).

Explicitly defining all the filter of a chain is somewhat an overkill if one only wants to provide one or a few additional custom filter(s). An alternative approach, is to let Spring Security namespace setup common filters and have the application configuration add extra filter(s) somewhere along the chain. Below, I show how to use XML namespace support to setup custom filters for Digest authentication (while retaining Basic authentication for some URLs):

<http pattern="/secured**">
	<http-basic/>
	<intercept-url pattern="/secured**" access="ROLE_ADMIN"/>
</http>
	
<http pattern="/xsecured**" entry-point-ref="digestEntryPoint" >
	<custom-filter ref="digestFilter" position="BASIC_AUTH_FILTER"/>
	<intercept-url pattern="/xsecured**" access="ROLE_ADMIN"/>
</http>

Note that we are using multiple top-level <http> elements matching different URL prefixes (a feature introduced since Spring Security 3.x). The first <http> element is used to configure the Basic HTTP authentication, as discussed in previous sections, but this time restricting the URL prefix that is mapped and secured. The second <http> element is used to configure the Digest authentication and mapped to URL prefix /xsecured**.

The novel element for the configuration of digest authentication is the use of <custom-filter> to setup a filter that perform Digest authentication. Because the XML namespace handler for <http> element automatically creates all other filters that are required to make a fully functional chain -- namely, securityContextPersistenceHolder, exceptionTranslationFilter, and filterSecurityInterceptor, only the Digest authentication filter needs to be configured. For Basic authentication, the element <http-basic/> takes care of creating the authentication filter and configuring it in the chain -- type BasicAuthenticationFilter. For Digest authentication, explicitly configuration is required.

The filter for Digest authentication should be an instance of DigestAuthenticationFilter. Because it takes the same functional role as the BasicAuthenticationFilter, we have inserted in the chain in the same relative position. This is done by specifying attribute position="BASIC_AUTH_FILTER".

The actual definition of the DigestAuthenticationFilter is show below as bean digestFilter:

<beans:bean id="digestFilter" class="org.springframework.security.web.authentication.www.DigestAuthenticationFilter">
  <beans:property name="userDetailsService" ref="userService"/>
  <beans:property name="authenticationEntryPoint" ref="digestEntryPoint"/>
</beans:bean>

The property userDetailsService is a reference to the user service that provides credentials information. The same user credential database is being used for Basic and Digest authentication. So a reference to the same bean can be used (repeated below):

<authentication-manager>
  <authentication-provider>
    <user-service id="userService">
      ...
    </user-service>
  </authentication-provider>
</authentication-manager>

The other dependency of digestFilter is an instance of DigestAuthenticationEntryPoint. This is the bean responsible to setup the HTTP headers for Unauthorized responses, when the user does not provide adequate credentials information in the Authorization header. Below, I show the configuration of DigestAuthenticationEntryPoint as bean digestEntryPoint:

<beans:bean id="digestEntryPoint" class="org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint">
  <beans:property name="realmName" value="My Digest Secure REST-WS"/>
  <beans:property name="key" value="somenouncekey"/>
  <beans:property name="nonceValiditySeconds" value="10"/>
</beans:bean>

Property realmName specifies the identifier that is shown to the user when requested for credentials. It also needs to be included in security token by clients. Property key is a random string used as an additional piece of data insert in the nonces generated by the server. Property nonceValiditySeconds specifies the validity of server nonces. Smaller values imply more secure applications, but also that the client will get more Unauthorized messages asking to "refresh" the authentication with a new nonce.

Probably you have noticed that the bean digestEntryPoint is specified also in attribute entry-point-ref="digestEntryPoint" of element <http>. This is required because the digestEntryPoint is also a dependency for the exceptionTranslationFilter create by <http>.

At this point we are ready to deploy the REST-WS with the extra method secured with Digest authentication (in addition to previous method secured with the simpler Basic authentication). An worthy exercise is to try to invoke the Digest secured method xsecured(), through URL /xsecured, in a client that only support Basic authentication. Below, we see the typical response of the server with HTTP status code 401 Unauthorized (captured and proxied with Eclipse TCP/IP Monitor tool).

Server HTTP response Message for:

HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=CD4D053FEBCFA6577FC98721E403C33C; Path=/rest-security; HttpOnly
WWW-Authenticate: Digest realm="My Digest Secure REST-WS", qop="auth", nonce="MTMxNzA1MDEwOTk0MTo0MmZhNWIyMjkzYWQwN2U3MGM2YjY1N2UzYTZhMWM3NA=="
Content-Type: text/html;charset=utf-8
Content-Length: 1187
Date: Mon, 26 Sep 2011 15:14:59 GMT

Because the server response includes the header WWW-Authenticate: Digest, we can make a good guess that the DigestAuthenticationEntryPoint and the filter chain for Digest authentication is setup appropriately. The next, and final, step is to invoke the REST-WS with a Digest authentication aware client.

REST Client with Digest Authentication

Fortunately for us, Apache Commons HttpClient as built-in support not only for Basic HTTP authentication, but also Digest HTTP authentication. Moreover, HttpClient is able to detect from the first server response which authentication protocol is mandated by the server for a particular URL. This means that we can use the same custom implementation SecureCommonsClientHttpRequestFactory that was used for Basic authentication.

Digest HTTP Client Authenthication with Apache Commons HttpClient

Below, I show a sample of the HTTP messages exchanged between a REST-WS client, implemented with RestTemplate configured with SecureCommonsClientHttpRequestFactory, and the server:

Client HTTP First Request Message:

GET /rest-security/xsecured HTTP/1.1
Accept: text/plain, */*
User-Agent: Jakarta Commons-HttpClient/3.1
Host: localhost:8080

Server HTTP First Response Message:

HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=214B34FA1CA1EAD298A88405DF1658F9; Path=/rest-security; HttpOnly
WWW-Authenticate: Digest realm="My Digest Secure REST-WS", qop="auth", nonce="MTMxNzIyNzEzNjA5MTo5YTM4MTAwOTIxZTI4MWEyOGNkMGI4ZTcyMzM1ODYzMA=="
Content-Type: text/html;charset=utf-8
Content-Length: 1187
Date: Wed, 28 Sep 2011 16:25:26 GMT

Note, again, that the first HTTP response sent by the server has status code 401 Unauthorized. This is because the client's first request does not have the appropriate Authorization header. Fortunately, the server provides in the HTTP header WWW-Authenticate all the information the client needs to build the Authorization header, including: the nonce generated by Spring Security, the realm name that was configured in the <http> XML element, and the quality-of-protocol (qop) attribute that for Spring Security is always auth.

In the second HTTP request sent by the client, the header Authorization is set by Apache HttpClient as expected by the Digest secured server. The client request also includes a client generated nonce, as header attribute cnonce, for increased security.

Client HTTP Second Request Message:

GET /rest-security/xsecured HTTP/1.1
Accept: text/plain, */*
User-Agent: Jakarta Commons-HttpClient/3.1
Authorization: Digest username="admin", realm="My Digest Secure REST-WS", nonce="MTMxNzIyNzEzNjA5MTo5YTM4MTAwOTIxZTI4MWEyOGNkMGI4ZTcyMzM1ODYzMA==", uri="/rest-security/xsecured", response="4f3e4771bb5f8649b955cc2b5269df4c", qop=auth, nc=00000001, cnonce="bf671b559bf53a77b7d77a7b4ffaf674"
Host: localhost:8080
Cookie: $Version=0; JSESSIONID=214B34FA1CA1EAD298A88405DF1658F9; $Path=/rest-security

The Spring Security enable server recognizes the client's effort to sent the correct Digest authentication information, and responds with the status code 200 OK and the return value of the REST-WS method xsecured() marshalled in the body of the response.

Server HTTP Second Response Message:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: JSESSIONID=2BCF410FB3422FD6D68691E32FB331FC; Path=/rest-security; HttpOnly
Content-Type: text/plain
Content-Length: 39
Date: Wed, 28 Sep 2011 16:25:26 GMT

XSECURED: Wed Sep 28 17:25:26 WEST 2011

Summary and Conclusions

I have shown how to implement REST-WS based on Spring Security and RestTemplate. Configuring Spring security for Basic HTTP authentication is very simple, due to the provided namespace support. Digest HTTP authentication requires a bit more configuration. On the client side, I used custom ClientHttpRequestFactory to configure a RestTemplate instance to have the client send authentication information as requested or expected by the server. Implementing Basic authentication is simple enough that security header can be set using a HttpConnection. For Digest authentication it is more convenient to use Apache HttpClient library to set HTTP headers.

Exercises to the Reader

Below, I present some suggestions to possible extensions to approach outline in this article to secure REST-WS based on Spring Framework RestTemplate. Features that requires extra work and/or are more difficult to implement are marked with *.

  • All the beans required to implement RestClient(s) , such as RestTemplate, particular types of ClientHttpRequestFactory, and CredentialsProvider were created and configured in Java code. Since we are using Spring Framework both on the the client and server side, we can also define these bean in a Spring Bean definition file. Create one or more Spring beans XML files to perform the configuration of the client. Use Spring class ClassPathXmlApplicationContext to read the XML files and configure the client application.
  • Note that BasicSecureSimpleClientHttpRequestFactory and SecureCommonsClientHttpRequestFactory are not passing the realm name provided by the server to the CrendentialProvider. This is because the client is using preemptive authentication -- that is, it sends the authentication information on the first (and only) request. Read the documentation of HttpClient and see how this could be fixed for SecureCommonsClientHttpRequestFactory.
  • I have used a very simple implementation of a user credentials provider that provides a pre-configured username and a password. A more general approach is to have an implementation based on GUI modal dialog, that asks the user for a name and password. Using your favorite widget toolkit for Java, such as Swing or SWT, implement a GUI based implementation of a credentials provider.
  • *I showed how to use Apache HttpClient to implement Digest HTTP authentication. Implementing Digest based authentication directly with Java HttpConnection is much more tricky and requires detailed study of the Digest HTTP authentication header. Implement a class DigestSecureSimpleClientHttpRequestFactory that does this. To perform the digest computations based on MD5 algorithm use the Java security static method MessageDigest.getInstance("MD5").

Resources

External References

Software

Acknowledgements

If you have any comments or suggestions that you think may improve this document please send an e-mail to the author.

Sponsors (contact to be a sponsor)
(c) Jorge Simão, 2013