Automatically add CORS configuration to Spring controllers
Tags: spring, cors
Published: 2022-05-20 16:31:58

> It could be useful to add with a less of code the expected CORS headers in your Spring boot configuration. Let's do this!

[CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) (Cross-Origin Resource Sharing) is a mechanism that allows "to protect" your resources APIs from undesirated calls. By saying protect I mean restrict the resources to be requested by any domain outside the origin domain where the resources was served.

## Flag all resources with @CrossOrigin

This is the only step required by the _configurer_ to claim the cross origin resource sharing. For example :
```
@CrossOrigin
@RestController
@RequestMapping(value = "/api/user")
public interface UserController { ...
```

## Declare the CORS Spring configurer

This Spring bean is responsible of several things 🔎 The first one action is to scan your resources looking for the paths will should be protected. In this example we use the autowired bean `ApplicationContext` as entry point in the Spring application context :

```
@Autowired
public CorsConfig(ApplicationContext applicationContext) {
    corsControllers = Stream.of(applicationContext.getBeanNamesForAnnotation(CrossOrigin.class))
            .filter(Objects::nonNull)
            .map(bean -> applicationContext.findAnnotationOnBean(bean, RequestMapping.class))
            .filter(mapping -> Objects.nonNull(mapping) && mapping.path().length > 0)
            .map(mapping -> mapping.path()[0])
            .peek(path -> log.info("Register CORS configuration for \"{}\".", path))
            .collect(toSet());
}
```
> _Please keep this code safe !_

The second one action is to declare `WebMvcConfigurer` bean will add the protected path in the [CORS Registry](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/CorsRegistry.html).

```
@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            for (String corsController : corsControllers) {
                registry.addMapping(corsController)
                        .allowedOriginPatterns(allowedCorsOrigin)
                        .allowedHeaders("Authorization", ...)
                        .allowedMethods("OPTIONS", "PUT", "POST", ...);
            }
        }
    };
}
```
Something interresting to note there is that we can externalize the declaration of allowed domains by Ant patterns in an array of String. The property **allowedCorsOrigin** is a variable annoted by @Value (see the sample code).

## Test the Spring configuration with CURL

Now we are able to test the configuration in a real Spring app.

🟢Here we use a valid domain for test :

```
❯ curl -H "Origin: https://fr.reservoircode.net" --verbose http://localhost:9999/api/user
*   Trying 127.0.0.1:9999...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9999 (#0)
> GET /api/user HTTP/1.1
> Host: localhost:9999
> User-Agent: curl/7.68.0
> Accept: */*
> Origin: https://fr.reservoircode.net
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Access-Control-Allow-Origin: https://fr.reservoircode.net
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Fri, 20 May 2022 16:20:17 GMT
< 
* Connection #0 to host localhost left intact
{"id":"1","name":"Ulrich"}
```

🔴Here we use a invalid domain for test :

```
❯ curl -H "Origin: https://foo.com" --verbose http://localhost:9999/api/user       
*   Trying 127.0.0.1:9999...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9999 (#0)
> GET /api/user HTTP/1.1
> Host: localhost:9999
> User-Agent: curl/7.68.0
> Accept: */*
> Origin: https://foo.com
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 403 
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< Transfer-Encoding: chunked
< Date: Fri, 20 May 2022 16:23:02 GMT
< 
* Connection #0 to host localhost left intact
Invalid CORS request
```

## Let me try it

If you want a Spring boot example you can clone the repository [https://github.com/ulrich/spring-cors-autoconfig](https://github.com/ulrich/spring-cors-autoconfig)

Crédit photo : https://pixabay.com/fr/users/jackmac34-483877/