If you're working on a Spring Security (and especially an OAuth) implementation, definitely have a look at the Learn Spring Security course:
>> LEARN SPRING SECURITYMocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.
Get started with mocking and improve your application tests using our Mockito guide:
Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.
Get started with understanding multi-threaded applications with our Java Concurrency guide:
Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:
Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.
But these can also be overused and fall into some common pitfalls.
To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:
Get started with Spring and Spring Boot, through the Learn Spring course:
>> LEARN SPRINGExplore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:
Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.
I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.
You can explore the course here:
Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.
Get started with Spring Data JPA through the guided reference course:
Refactor Java code safely β and automatically β with OpenRewrite.
Refactoring big codebases by hand is slow, risky, and easy to put off. Thatβs where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.
Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions β one for newcomers and one for experienced users. Youβll see how recipes work, how to apply them across projects, and how to modernize code with confidence.
Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.
Table of Contents
- 1. Overview
- 2. Configuration of Basic Authentication
- 2.1. Satisfying the stateless constraint β getting rid of sessions
- 3. Configuration of Digest Authentication
- 4. Supporting both authentication protocols in the same RESTful service
- 4.1. Anonymous request
- 4.2. Request with authentication credentials
- 5. Testing both scenarios
- 6. Conclusion
1. Overview
This article discusses how to set up both Basic and Digest Authentication on the same URI structure of a REST API. In a previous article, we discussed another method of securing the REST Service β form-based authentication, so Basic and Digest authentication is the natural alternative, as well as the more RESTful one.
2. Configuration of Basic Authentication
The main reason that form-based authentication is not ideal for a RESTful Service is that Spring Security will make use of Sessions β this is of course state on the server, so the statelessness constraints in REST is practically ignored.
Weβll start by setting up Basic Authentication β first we remove the old custom entry point and filter from the main <http> security element:
<http create-session="stateless">
<intercept-url pattern="/api/admin/**" access="ROLE_ADMIN" />
<http-basic />
</http>
Note how support for basic authentication has been added with a single configuration line β <http-basic /> β which handles the creation and wiring of both the BasicAuthenticationFilter and the BasicAuthenticationEntryPoint.
2.1. Satisfying the Stateless Constraint β Getting Rid of Sessions
One of the main constraints of the RESTful architectural style is that the client-server communication is fully stateless, as the original dissertation reads:
5.1.3 Stateless
We next add a constraint to the client-server interaction: communication must be stateless in nature, as in the client-stateless-server (CSS) style of Section 3.4.3 (Figure 5-3), such that each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server. Session state is therefore kept entirely on the client.
The concept of Session on the server is one with a long history in Spring Security, and removing it entirely has been difficult until now, especially when the configuration was done by using the namespace.
However, Spring Security augments the namespace configuration with a new stateless option for session creation, which effectively guarantees that no session will be created or used by Spring. What this new option does is complete removes all session related filters from the security filter chain, ensuring that authentication is performed for each request.
3. Configuration of Digest Authentication
Starting with the previous configuration, the filter and entry point necessary to set up digest authentication will be defined as beans. Then, the digest entry point will override the one created by <http-basic> behind the scenes. Finally, the custom digest filter will be introduced in the security filter chain using the after semantics of the security namespace to position it directly after the basic authentication filter.
<http create-session="stateless" entry-point-ref="digestEntryPoint">
<intercept-url pattern="/api/admin/**" access="ROLE_ADMIN" />
<http-basic />
<custom-filter ref="digestFilter" after="BASIC_AUTH_FILTER" />
</http>
<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>
<authentication-manager>
<authentication-provider>
<user-service id="userService">
<user name="eparaschiv" password="eparaschiv" authorities="ROLE_ADMIN" />
<user name="user" password="user" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
Unfortunately, there is no support in the security namespace to automatically configure the digest authentication the way basic authentication can be configured with <http-basic>. Because of that, the necessary beans had to be defined and wired manually into the security configuration.
4. Supporting Both Authentication Protocols in the Same Restful Service
Basic or Digest authentication alone can be easily implemented in Spring Security; it is supporting both of them for the same RESTful web service, on the same URI mappings that introduces a new level of complexity into the configuration and testing of the service.
4.1. Anonymous Request
With both basic and digest filters in the security chain, the way an anonymous request β a request containing no authentication credentials (Authorization HTTP header) β is processed by Spring Security is β the two authentication filters will find no credentials and will continue execution of the filter chain. Then, seeing how the request wasnβt authenticated, an AccessDeniedException is thrown and caught in the ExceptionTranslationFilter, which commences the digest entry point, prompting the client for credentials.
The responsibilities of both the basic and digest filters are very narrow β they will continue to execute the security filter chain if they are unable to identify the type of authentication credentials in the request. It is because of this that Spring Security can have the flexibility to be configured with support for multiple authentication protocols on the same URI.
When a request is made containing the correct authentication credentials β either basic or digest β that protocol will be rightly used. However, for an anonymous request, the client will get prompted only for digest authentication credentials. This is because the digest entry point is configured as the main and single entry point of the Spring Security chain; as such digest authentication can be considered the default.
4.2. Request With Authentication Credentials
A request with credentials for Basic authentication will be identified by the Authorization header starting with the prefix βBasicβ. When processing such a request, the credentials will be decoded in the basic authentication filter and the request will be authorized. Similarly, a request with credentials for Digest authentication will use the prefix βDigestβ for itβs Authorization header.
5. Testing Both Scenarios
The tests will consume the REST service by creating a new resource after authenticating with either basic or digest:
@Test
public void givenAuthenticatedByBasicAuth_whenAResourceIsCreated_then201IsReceived(){
// Given
// When
Response response = given()
.auth().preemptive().basic( ADMIN_USERNAME, ADMIN_PASSWORD )
.contentType( HttpConstants.MIME_JSON ).body( new Foo( randomAlphabetic( 6 ) ) )
.post( paths.getFooURL() );
// Then
assertThat( response.getStatusCode(), is( 201 ) );
}
@Test
public void givenAuthenticatedByDigestAuth_whenAResourceIsCreated_then201IsReceived(){
// Given
// When
Response response = given()
.auth().digest( ADMIN_USERNAME, ADMIN_PASSWORD )
.contentType( HttpConstants.MIME_JSON ).body( new Foo( randomAlphabetic( 6 ) ) )
.post( paths.getFooURL() );
// Then
assertThat( response.getStatusCode(), is( 201 ) );
}
Note that the test using basic authentication adds credentials to the request preemptively, regardless if the server has challenged for authentication or not. This is to ensure that the server doesnβt need to challenge the client for credentials, because if it did, the challenge would be for Digest credentials, since that is the default.
6. Conclusion
This article covered the configuration and implementation of both Basic and Digest authentication for a RESTful service, using mostly Spring Security namespace support as well as some new features in the framework.
