diff --git a/spring-web/src/main/java/org/springframework/web/cors/reactive/PreFlightRequestHandler.java b/spring-web/src/main/java/org/springframework/web/cors/reactive/PreFlightRequestHandler.java new file mode 100644 index 0000000000..bf94e99d68 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/web/cors/reactive/PreFlightRequestHandler.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2021 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.web.cors.reactive; + +import reactor.core.publisher.Mono; + +import org.springframework.web.server.ServerWebExchange; + +/** + * Handler for CORS pre-flight requests. + * + * @author Rossen Stoyanchev + * @since 5.3.4 + */ +public interface PreFlightRequestHandler { + + /** + * Handle a pre-flight request by finding and applying the CORS configuration + * that matches the expected actual request. As a result of handling, the + * response should be updated with CORS headers or rejected with + * {@link org.springframework.http.HttpStatus#FORBIDDEN}. + * @param exchange the exchange for the request + * @return a completion handle + */ + Mono handlePreFlight(ServerWebExchange exchange); + +} diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/DispatcherHandler.java b/spring-webflux/src/main/java/org/springframework/web/reactive/DispatcherHandler.java index 604b9e18a0..29d291b72f 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/DispatcherHandler.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/DispatcherHandler.java @@ -29,10 +29,10 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.http.HttpStatus; -import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; import org.springframework.web.cors.reactive.CorsUtils; +import org.springframework.web.cors.reactive.PreFlightRequestHandler; import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebHandler; @@ -53,9 +53,10 @@ import org.springframework.web.server.adapter.WebHttpHandlerBuilder; * *

{@code DispatcherHandler} is also designed to be a Spring bean itself and * implements {@link ApplicationContextAware} for access to the context it runs - * in. If {@code DispatcherHandler} is declared with the bean name "webHandler" - * it is discovered by {@link WebHttpHandlerBuilder#applicationContext} which - * creates a processing chain together with {@code WebFilter}, + * in. If {@code DispatcherHandler} is declared as a bean with the name + * "webHandler", it is discovered by + * {@link WebHttpHandlerBuilder#applicationContext(ApplicationContext)} which + * puts together a processing chain together with {@code WebFilter}, * {@code WebExceptionHandler} and others. * *

A {@code DispatcherHandler} bean declaration is included in @@ -68,7 +69,7 @@ import org.springframework.web.server.adapter.WebHttpHandlerBuilder; * @since 5.0 * @see WebHttpHandlerBuilder#applicationContext(ApplicationContext) */ -public class DispatcherHandler implements WebHandler, ApplicationContextAware { +public class DispatcherHandler implements WebHandler, PreFlightRequestHandler, ApplicationContextAware { @Nullable private List handlerMappings; @@ -142,6 +143,9 @@ public class DispatcherHandler implements WebHandler, ApplicationContextAware { if (this.handlerMappings == null) { return createNotFoundError(); } + if (CorsUtils.isPreFlightRequest(exchange.getRequest())) { + return handlePreFlight(exchange); + } return Flux.fromIterable(this.handlerMappings) .concatMap(mapping -> mapping.getHandler(exchange)) .next() @@ -158,11 +162,8 @@ public class DispatcherHandler implements WebHandler, ApplicationContextAware { } private Mono invokeHandler(ServerWebExchange exchange, Object handler) { - // No handling for CORS rejected requests and pre-flight requests - ServerHttpRequest request = exchange.getRequest(); - HttpStatus status = exchange.getResponse().getStatusCode(); - if (ObjectUtils.nullSafeEquals(status, HttpStatus.FORBIDDEN) || CorsUtils.isPreFlightRequest(request)) { - return Mono.empty(); + if (ObjectUtils.nullSafeEquals(exchange.getResponse().getStatusCode(), HttpStatus.FORBIDDEN)) { + return Mono.empty(); // CORS rejection } if (this.handlerAdapters != null) { for (HandlerAdapter handlerAdapter : this.handlerAdapters) { @@ -196,4 +197,13 @@ public class DispatcherHandler implements WebHandler, ApplicationContextAware { throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue()); } + @Override + public Mono handlePreFlight(ServerWebExchange exchange) { + return Flux.fromIterable(this.handlerMappings != null ? this.handlerMappings : Collections.emptyList()) + .concatMap(mapping -> mapping.getHandler(exchange)) + .switchIfEmpty(Mono.fromRunnable(() -> exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN))) + .next() + .then(); + } + }