Resolving CORS errors in spring

CORS stands for Cross-Origin Resource Sharing. It is a security mechanism for restricting requests coming from different origins, hence enables controlled access to resource located outside a given domain. It is critical to properly configure CORS since if poorly configured it provides potential for cross-domain attacks.

In this article we are going to discuss about CORS configuration the right way in the spring context. Many developers implementation of CORS configurations may contain mistakes or be lenient in an attempt to make everything work smoothly and this can result in major vulnerabilities.

To visualize CORS error here is an example of one I faced when integrating a client built with angular framework.

CORS error

Approaches to handle CORS errors:

  1. Using @CrossOrigin annotation at the controller class

This annotation can be applied on the class or method level. When applied on the controller class it applies to all of the methods in the controller class. Likewise when applied on a controller method eg. a get mapping it will only apply to that specific method. This is illlustrated below.

@CrossOrigin(origins = "*")
@GetMapping("/get-resource/{carId}")
public Car getCarDetails(@PathVariable long carId){
return carService.getCar(carId);
}

This annotation can be appended on top of methods in controller classes to allow the specific method to be accessible by front end applications. The asterix (*) in the annotation implies it accepts requests from all origins.

The best approach might be to add the known origin you want to allow. eg

@CrossOrigin(origins = "localhost:4200")
@GetMapping("/get-resource/{carId}")
public Car getCarDetails(@PathVariable long carId){
return carService.getCar(carId);
}

This enables requests from angular clients only running on port 4200 that are local to the server. If a requests comes from a different origin it will throw a CORS error. It is best to apply the exact url domain of your client on the origins parameter to enable only requests from your known clients.

To allow requests from multiple known clients, do this:

@CrossOrigin(origins = {"http://localhost:4200", "http://someserver:8080"})

For class level configuration it can be similarly used as follows:

@CrossOrigin(origins = "localhost:4200")
@RestController
public class TransactionsController {

By default @CrossOrigin allows all origins, all headers, and the HTTP methods specified and a maxAge of 30 minutes. This attributes can be customized by adding specified values. Default values appear as follows:

@CrossOrigin(origins = "*", methods = "*", allowedHeaders = "*", exposedHeaders = "*", allowCredentials = "*", maxAge = 30)

Check out spring docs for more info here.

2. Global configuration

For the case above it only configures for specific methods or classes and incase you want to configure all APIs in your project with CORS a lot of boilerplate code may be added if you use the first approach. To create a global configuration with less boilerplate, the following approach can be followed.

To do this simply add CorsConfigurationSource bean to the project preferably on your security configuration class,

@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();

configuration.setAllowedOrigins( Arrays.asList("http://localhost:4200"));

Again since we are world class developers do not leave the origin with an asterix since it allows all origins. You can custom configure everything depending on values you want to set on the other configurations. Once done add it to your security filter.

protected void configure(HttpSecurity http) throws Exception {
http.cors()
.configurationSource(corsConfigurationSource())
.and()
.csrf()
.disable()
...
}

And we are done configuring CORS globally.

To learn more on this implementation checkout springs documentation here.

If you have made it this far always strive to allow known origins for your applications security sake.

Happy coding guys!!

Solving real world problems through technology