I usually post about Security on Twitter - you can follow me there:
This tutorial shows how to set up, configure and customize Digest Authentication with Spring. Similar to the previous article covering Basic Authentication, we’re going to built on top of the Spring MVC tutorial, and secure the application with the Digest Auth mechanism provided by Spring Security.
2. The Security XML Configuration
First thing to understand about the configuration is that, while Spring Security does have full out of the box support for the Digest authentication mechanism, this support is not as well integrated into the namespace as Basic Authentication was.
In this case, we need to manually define the raw beans that are going to make up the security configuration – the DigestAuthenticationFilter and the DigestAuthenticationEntryPoint:
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation=" http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"> <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> <beans:bean id="digestEntryPoint" class="org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint"> <beans:property name="realmName" value="Contacts Realm via Digest Authentication" /> <beans:property name="key" value="acegi" /> </beans:bean> <!-- the security namespace configuration --> <http use-expressions="true" entry-point-ref="digestEntryPoint"> <intercept-url pattern="/**" access="isAuthenticated()" /> <custom-filter ref="digestFilter" after="BASIC_AUTH_FILTER" /> </http> <authentication-manager> <authentication-provider> <user-service id="userService"> <user name="user1" password="user1Pass" authorities="ROLE_USER" /> </user-service> </authentication-provider> </authentication-manager> </beans:beans>
Next, we need to integrate these beans into the overall security configuration – and in this case, the namespace is still flexible enough to allow us to do that.
The first part of this is pointing to the custom entry point bean, via the entry-point-ref attribute of the main <http> element.
The second part is adding the newly defined digest filter into the security filter chain. Since this filter is functionally equivalent to the BasicAuthenticationFilter, we are using the same relative position in the chain – this is specified by the BASIC_AUTH_FILTER alias in the overall Spring Security Standard Filters.
Finally, notice that the Digest Filter is configured to point to the user service bean – and here, the namespace is again very useful as it allows us to specify a bean name for the default user service created by the <user-service> element:
3. Consuming the Secured Application
We’re going to be using the curl command to consume the secured application and understand how a client can interact with it.
Let’s start by requesting the homepage – without providing security credentials in the request:
curl -i http://localhost/spring-security-mvc-digest-auth/homepage.html
As expected, we get back a response with a 401 Unauthorized status code:
HTTP/1.1 401 Unauthorized Server: Apache-Coyote/1.1 Set-Cookie: JSESSIONID=CF0233C...; Path=/spring-security-mvc-digest-auth/; HttpOnly WWW-Authenticate: Digest realm="Contacts Realm via Digest Authentication", qop="auth", nonce="MTM3MzYzODE2NTg3OTo3MmYxN2JkOWYxZTc4MzdmMzBiN2Q0YmY0ZTU0N2RkZg==" Content-Type: text/html;charset=utf-8 Content-Length: 1061 Date: Fri, 12 Jul 2013 14:04:25 GMT
If this request were sent by the browser, the authentication challenge would prompt the user for credentials using a simple user/password dialog.
Let’s now provide the correct credentials and send the request again:
curl -i --digest --user user1:user1Pass http://localhost/spring-security-mvc-digest-auth/homepage.html
Notice that we are enabling Digest Authentication for the curl command via the –digest flag.
The first response from the server will be the same – the 401 Unauthorized – but the challenge will now be interpreted and acted upon by a second request – which will succeed with a 200 OK:
HTTP/1.1 401 Unauthorized Server: Apache-Coyote/1.1 Set-Cookie: JSESSIONID=A961E0D...; Path=/spring-security-mvc-digest-auth/; HttpOnly WWW-Authenticate: Digest realm="Contacts Realm via Digest Authentication", qop="auth", nonce="MTM3MzYzODgyOTczMTo3YjM4OWQzMGU0YTgwZDg0YmYwZjRlZWJjMDQzZWZkOA==" Content-Type: text/html;charset=utf-8 Content-Length: 1061 Date: Fri, 12 Jul 2013 14:15:29 GMT HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Set-Cookie: JSESSIONID=55F996B...; Path=/spring-security-mvc-digest-auth/; HttpOnly Content-Type: text/html;charset=ISO-8859-1 Content-Language: en-US Content-Length: 90 Date: Fri, 12 Jul 2013 14:15:29 GMT <html> <head></head> <body> <h1>This is the homepage</h1> </body> </html>
A final note on this interaction is that a client can preemptively send the correct Authorization header with the first request, and thus entirely avoid the server security challenge and the second request.
4. The Maven Dependencies
The security dependencies are discussed in depth in the Spring Security Maven tutorial. In short, we will need to define spring-security-web and spring-security-config as dependencies in our pom.xml.
In this tutorial we introduce security into a simple Spring MVC project by leveraging the Digest Authentication support in the framework.
The implementation of these examples can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.
When the project runs locally, the homepage html can be accessed at (or, with minimal Tomcat configuration, on port 80):
Finally, there is no reason an application needs to choose between Basic and Digest authentication – both can be set up simultaneously on the same URI structure, in such a way that the client can pick between the two mechanisms when consuming the web application.