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.
1. Overview
In this short tutorial, weβre going to learn how to solve the error βResponse for preflight has invalid HTTP status code 401β, which can occur in applications that support cross-origin communication and use Spring Security.
First, weβll see what cross-origin requests are and then weβll fix a problematic example.
2. Cross-Origin Requests
Cross-origin requests, in short, are HTTP requests where the origin and the target of the request are different. This is the case, for instance, when a web application is served from one domain and the browser sends an AJAX request to a server in another domain.
To manage cross-origin requests, the server needs to enable a particular mechanism known as CORS, or Cross-Origin Resource Sharing.
The first step in CORS is an OPTIONS request to determine whether the target of the request supports it. This is called a pre-flight request.
The server can then respond to the pre-flight request with a collection of headers:
- Access-Control-Allow-Origin: Defines which origins may have access to the resource. A β*β represents any origin
- Access-Control-Allow-Methods: Indicates the allowed HTTP methods for cross-origin requests
- Access-Control-Allow-Headers: Indicates the allowed request headers for cross-origin requests
- Access-Control-Max-Age: Defines the expiration time of the result of the cached preflight request
So, if the pre-flight request doesnβt meet the conditions determined from these response headers, the actual follow-up request will throw errors related to the cross-origin request.
Itβs easy to add CORS support to our Spring-powered service, but if configured incorrectly, this pre-flight request will always fail with a 401.
3. Creating a CORS-enabled REST API
To simulate the problem, letβs first create a simple REST API that supports cross-origin requests:
@RestController
@CrossOrigin("http://localhost:4200")
public class ResourceController {
@GetMapping("/user")
public String user(Principal principal) {
return principal.getName();
}
}
The @CrossOrigin annotation makes sure that our APIs are accessible only from the origin mentioned in its argument.
4. Securing Our REST API
Letβs now secure our REST API with Spring Security:
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults());
return http.build();
}
}
In this configuration class, weβve enforced authorization to all incoming requests. As a result, it will reject all requests without a valid authorization token.
5. Making a Pre-flight Request
Now that weβve created our REST API, letβs try a pre-flight request using curl:
curl -v -H "Access-Control-Request-Method: GET" -H "Origin: http://localhost:4200"
-X OPTIONS http://localhost:8080/user
...
< HTTP/1.1 401
...
< WWW-Authenticate: Basic realm="Realm"
...
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Access-Control-Allow-Origin: http://localhost:4200
< Access-Control-Allow-Methods: POST
< Access-Control-Allow-Credentials: true
< Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
...
From the output of this command, we can see that the request was denied with a 401.
Since this is a curl command, we wonβt see the error βResponse for preflight has invalid HTTP status code 401β in the output.
But we can reproduce this exact error by creating a front end application that consumes our REST API from a different domain and running it in a browser.
6. The Solution
We havenβt explicitly excluded the preflight requests from authorization in our Spring Security configuration. Remember that Spring Security secures all endpoints by default.
As a result, our API expects an authorization token in the OPTIONS request as well.
Spring provides an out of the box solution to exclude OPTIONS requests from authorization checks:
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ...
http.cors(Customizer.withDefaults()); // disable this line to reproduce the CORS 401
return http.build();
}
}
The cors() method will add the Spring-provided CorsFilter to the application context, bypassing the authorization checks for OPTIONS requests.
Now we can test our application again and see that itβs working.
7. Conclusion
In this short article, weβve learned how to fix the error βResponse for preflight has an invalid HTTP status code 401β, which is linked with Spring Security and cross-origin requests.
Note that, with the example, the client and the API should run on different domains or ports to recreate the problem. For instance, we can map the default hostname to the client and the machine IP address to our REST API when running on a local machine.
