Cross-Origin Resource Sharing (CORS) in Spring Boot Applications
In this article, we will explore CORS, what it is, how it works, and how to implement it in a Spring Boot application with the help of Spring Security.
What is CORS?
CORS stands for Cross-Origin Resource Sharing. To understand CORS, we must first know how browsers behave in this context. Normally, browsers don’t allow front-end client applications to make requests to backend servers that are not hosted in the same domain as the site.
To understand this better, let’s look at an example. Say your client application is hosted on the domain “carrot.com”. If your application makes any requests to other domains, such as “tomato.com”, the browser will block the action. The browser will allow the client app to make requests only to applications hosted on “carrot.com”.
Shocking, right? Then how do modern applications work? We know that most front-end applications are built using frameworks like ReactJS, Angular, etc. These are hosted separately from the backend servers and make REST calls to these servers. This wouldn’t be possible if the browser behaved as discussed above, right?
This is where CORS comes into the picture to make this possible.
How CORS Works?
When the client app (hosted on “carrot.com”) makes a request to a backend server (hosted on “tomato.com”), the server will attach a header called Access-Control-Allow-Origin
with "carrot.com" as its value in the response.
When the browser receives the response, it checks for the presence of the Access-Control-Allow-Origin
header with the appropriate value. If this header is present and allows "carrot.com", the browser relaxes its strict origin policy and permits the client app to process the response.
The server maintains a list of domains for which it intends the browser to permit client applications to access its resources.
💡Did you notice?
The browser’s strict origin policy doesn’t prevent the client application from making requests to cross-origin sites. Instead, it blocks the client application from accessing the response sent by cross-origin applications if the appropriate headers are not present. Therefore, CORS is not a typical API endpoint security mechanism.
Implementing CORS in Spring boot applications
With an understanding of how CORS works, let’s proceed to implement it in a Spring Boot application.
Before we do that, let’s first observe how the browser blocks cross-origin requests in practice. To demonstrate this, we will create a Spring MVC project with just two endpoints: one to serve an HTML page, and another that the JavaScript in the HTML page will call.
But the trick is, we’ll call the first endpoint using the host localhost. After the page loads, we’ll attempt to call the second endpoint using the host 127.0.0.1. Despite both endpoints pointing to the same application server, the browser treats these host names as different origins and applies its CORS policy accordingly.
I have created a controller class named CorsDemo
in my Spring Boot application. It includes two endpoints: /
, which serves an HTML page, and /test
, which is invoked by the HTML page.
@Controller
public class CorsDemo {
@GetMapping("/")
public String home() {
return "tester.html";
}
@PostMapping("/test")
@ResponseBody
public String test() {
return "Hello World!";
}
}
Additionally, I’ve developed an HTML file named tester.html
located in resources/templates
directory. This project utilizes Thymeleaf as the template engine.
<!DOCTYPE HTML>
<html>
<head>
<script>
const request = new XMLHttpRequest();
const url='http://127.0.0.1:8080/test';
request.open("POST", url);
request.send();
request.onreadystatechange = (e) => {
document
.getElementById("invoker")
.innerHTML = request.responseText;
}
</script>
</head>
<body>
<div id="invoker"></div>
</body>
</html>
Now, if we hit localhost:8080/
from our browser, we can see that the first call successfully fetches the HTML page. However, the second call (/test
) fails with a CORS error.
To avoid this issue, we can add the Spring Boot annotation @CrossOrigin
over the /test
endpoint.
@PostMapping("/test")
@ResponseBody
@CrossOrigin
public String test() {
return "Hello World!";
}
This annotation allows any domain to access the endpoint.
As you can see, there is an Access-Control-Allow-Origin
header in the response with the value *
, which means any domain can access this particular endpoint.
But it is difficult to annotate each and every endpoint in a large project. Therefore, we can use the CorsConfigurer
from the Spring Security.
I have created a custom CORS configuration source class that extends the CorsConfigurationSource
interface. Inside that class, I initialized a CorsConfiguration
object, specifying the allowed domains, allowed HTTP methods, and allowed headers. Only requests with the specified HTTP methods and headers will receive a response with the Access-Control-Allow-Origin
header.
@Component
public class CustomCorsConfiguration implements CorsConfigurationSource {
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:8080", "http://127.0.0.1:8080"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
config.setAllowedHeaders(List.of("*"));
return config;
}
}
In the security filter chain, we add the custom CORS configuration source bean to the CorsConfigurer
.
@Configuration
public class SecurityConfig {
@Autowired CustomCorsConfiguration customCorsConfiguration;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(req -> req.anyRequest().permitAll())
.cors(c -> c.configurationSource(customCorsConfiguration));
return httpSecurity.build();
}
}
Hope you like this article. For further exploration on Spring framework and Security-related topics, feel free to check out more articles on my profile.