1

I've read many posts regarding CORS in Spring (Boot) but none could answer my question, but maybe I just missed the answer, so bear with me. I have a REST Webservice currently used only for server to server calls. I now want to open some endpoints to be called directly from the browser, but not from the same domain, thus CORS. I got it working for some endpoints by doing two things: 1. enabling OPTIONS in my WebSecurityConfigurerAdapter:

http.authorizeRequests()
   .mvcMatchers(HttpMethod.OPTIONS, 
                "/endpont1", 
                "/endpoint2")
   .permitAll()

2. adding the following annotation to my @GetMapping for these endpoints:

@CrossOrigin(origins = "${cors.origin}", allowCredentials = "true", 
                         exposedHeaders = ResponseUtils.CONTENT_DISPOSITION)
@GetMapping("/endpoint1")

The problem is, as far as I understand the documentation, leaving origins empty allows CORS for any domain. And I don't want to allow OPTIONS if I don't need CORS.

What is the best way to make this configurable through a properties file?

The "embedded" application.properties should have it disabled, but if the tenant wants to enable it we can provide an additional application-tenant.properties where we could enable it for certain domains and start the application with the appropriate profile.

EDIT: I found an answer in another post which looks interesting and maybe I can do this conditionally: https://stackoverflow.com/a/43559288/3737177

Thomas
  • 6,325
  • 4
  • 30
  • 65

3 Answers3

2

After a few try and errors I found a working solution based on this answer:

@Configuration
@EnableConfigurationProperties
@Order(1)
public class EndpointSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private RequestMatcher requestMatcher;

    @Value("${cors.origins:}")
    private String corsOrigins;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        if (StringUtils.isNotBlank(corsOrigins)) {
            http.cors().configurationSource(buildConfigurationSource());
        }
        http.requestMatchers().mvcMatchers("/endpoint1", "/pendpoint2")
        .and().csrf().requireCsrfProtectionMatcher(requestMatcher)
        .and().authorizeRequests().anyRequest()
                .hasAnyRole(SecurityConfiguration.ROLE_ENDPOINT_USER, SecurityConfiguration.ROLE_ADMIN)
        .and().httpBasic();
    }

    private CorsConfigurationSource buildConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList(corsOrigins.split(",")));
        configuration.setAllowedMethods(Arrays.asList("GET");
        configuration.setAllowedHeaders(Arrays.asList("authorization"));
        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/endpoint1", configuration);
        source.registerCorsConfiguration("/endpoint2", configuration);
        return source;
    }

}

If there is a cors.origins property in the application-tenant.properties, it enables CORS and configures the allowed methods and headers. CSRF is also enabled for same origin requests.

Thomas
  • 6,325
  • 4
  • 30
  • 65
1

The truth is that you CANNOT set the global CORS configuration using the application.properties file. You HAVE TO use JavaConfig as described here.

implements WebMvcConfigurer

and override below method

 @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("http://domain4.com")
            .allowedMethods("PUT", "DELETE")
            .allowedHeaders("header1", "header2", "header3")
            .exposedHeaders("header1", "header2")
            .allowCredentials(false).maxAge(4200);
    }

Or Add below code snippet in Application.java

@Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
            }
        };
    }

I think this way you can add properties in a property file and use those in code here and based on different flavors you can override those properties.

Maheshwar Ligade
  • 6,709
  • 4
  • 42
  • 59
-1

For loading custom properties files you can use
spring.config.import=optional:classpath:cors.yml
or from java args
-Dspring.config.import=optional:classpath:cors.yml
This method support reading from file, from url, repository and config server
Spring documentation about this

For reading CORS configuration from properties file you may use library (I'm developer of this)

<dependency>
  <groupId>io.github.iruzhnikov</groupId>
  <artifactId>spring-webmvc-cors-properties-autoconfigure</artifactId>
  <version>VERSION</version>
</dependency>

and properties config

spring:
  web:
    cors:
      enabled: true
      mappings: #spring.web.cors.mappings.<any_name>.<property>: <value>
        anyName: #just any name, just for grouping properties under the same path pattern (not used in internal logic)
          paths: #ant style path pattern, ATTENTION! not ordered, /** pattern override all other pattern
            - /path/to/api
            - /path/to/api/**
          #allowed-origins: "*"
          allowed-methods: GET #Enable override all defaults! If disabled: a lot more from all the controller methods included from the path pattern matches
          #allowed-headers: "*"
          #exposed-headers: ('*' - not-supported)
          #allow-credentials: true
          allowed-origin-patterns: .*
          #max-age: PT30M