Update CORS support

This commit updates CORS support in order to check Origin header
in CorsUtils#isPreFlightRequest which does not change how Spring
MVC or WebFlux process CORS request but is more correct in term
of behavior since it is a public API potentially used in another
contexts.

It also removes an unnecessary check in
AbstractHandlerMethodMapping#hasCorsConfigurationSource and processes
every preflight request with PreFlightHandler.

Closes gh-24327
This commit is contained in:
Sébastien Deleuze
2020-01-10 14:51:55 +01:00
parent 8396e6bdd1
commit bc7d010485
10 changed files with 74 additions and 26 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -182,8 +182,8 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
if (logger.isDebugEnabled()) {
logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);
}
if (hasCorsConfigurationSource(handler)) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpRequest request = exchange.getRequest();
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(exchange) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -374,8 +374,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
@Override
protected boolean hasCorsConfigurationSource(Object handler) {
return super.hasCorsConfigurationSource(handler) ||
(handler instanceof HandlerMethod && this.mappingRegistry.getCorsConfiguration((HandlerMethod) handler) != null) ||
handler.equals(PREFLIGHT_AMBIGUOUS_MATCH);
(handler instanceof HandlerMethod && this.mappingRegistry.getCorsConfiguration((HandlerMethod) handler) != null);
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -70,7 +70,7 @@ public class CorsUrlHandlerMappingTests {
Object actual = this.handlerMapping.getHandler(exchange).block();
assertThat(actual).isNotNull();
assertThat(actual).isSameAs(this.welcomeController);
assertThat(actual).isNotSameAs(this.welcomeController);
}
@Test

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,11 +35,13 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.testfixture.http.server.reactive.bootstrap.HttpServer;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
/**
* Integration tests with {@code @CrossOrigin} and {@code @RequestMapping}
@@ -89,6 +91,28 @@ class CrossOriginAnnotationIntegrationTests extends AbstractRequestMappingIntegr
assertThat(entity.getBody()).isEqualTo("no");
}
@ParameterizedHttpServerTest
void optionsRequestWithAccessControlRequestMethod(HttpServer httpServer) throws Exception {
startServer(httpServer);
this.headers.clear();
this.headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
ResponseEntity<String> entity = performOptions("/no", this.headers, String.class);
assertThat(entity.getBody()).isNull();
}
@ParameterizedHttpServerTest
void preflightRequestWithoutAnnotation(HttpServer httpServer) throws Exception {
startServer(httpServer);
this.headers.add(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
try {
performOptions("/no", this.headers, Void.class);
fail("Preflight request without CORS configuration should fail");
}
catch (HttpClientErrorException ex) {
assertThat(ex.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
}
}
@ParameterizedHttpServerTest
void actualPostRequestWithoutAnnotation(HttpServer httpServer) throws Exception {
startServer(httpServer);