Expose DispatcherHandler as PreFlightRequestHandler
Closes gh-26257
This commit is contained in:
parent
729535d36c
commit
0fc8bf654b
|
@ -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<Void> handlePreFlight(ServerWebExchange exchange);
|
||||||
|
|
||||||
|
}
|
|
@ -29,10 +29,10 @@ import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationContextAware;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.web.cors.reactive.CorsUtils;
|
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.ResponseStatusException;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
import org.springframework.web.server.WebHandler;
|
import org.springframework.web.server.WebHandler;
|
||||||
|
@ -53,9 +53,10 @@ import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||||
*
|
*
|
||||||
* <p>{@code DispatcherHandler} is also designed to be a Spring bean itself and
|
* <p>{@code DispatcherHandler} is also designed to be a Spring bean itself and
|
||||||
* implements {@link ApplicationContextAware} for access to the context it runs
|
* implements {@link ApplicationContextAware} for access to the context it runs
|
||||||
* in. If {@code DispatcherHandler} is declared with the bean name "webHandler"
|
* in. If {@code DispatcherHandler} is declared as a bean with the name
|
||||||
* it is discovered by {@link WebHttpHandlerBuilder#applicationContext} which
|
* "webHandler", it is discovered by
|
||||||
* creates a processing chain together with {@code WebFilter},
|
* {@link WebHttpHandlerBuilder#applicationContext(ApplicationContext)} which
|
||||||
|
* puts together a processing chain together with {@code WebFilter},
|
||||||
* {@code WebExceptionHandler} and others.
|
* {@code WebExceptionHandler} and others.
|
||||||
*
|
*
|
||||||
* <p>A {@code DispatcherHandler} bean declaration is included in
|
* <p>A {@code DispatcherHandler} bean declaration is included in
|
||||||
|
@ -68,7 +69,7 @@ import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
* @see WebHttpHandlerBuilder#applicationContext(ApplicationContext)
|
* @see WebHttpHandlerBuilder#applicationContext(ApplicationContext)
|
||||||
*/
|
*/
|
||||||
public class DispatcherHandler implements WebHandler, ApplicationContextAware {
|
public class DispatcherHandler implements WebHandler, PreFlightRequestHandler, ApplicationContextAware {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private List<HandlerMapping> handlerMappings;
|
private List<HandlerMapping> handlerMappings;
|
||||||
|
@ -142,6 +143,9 @@ public class DispatcherHandler implements WebHandler, ApplicationContextAware {
|
||||||
if (this.handlerMappings == null) {
|
if (this.handlerMappings == null) {
|
||||||
return createNotFoundError();
|
return createNotFoundError();
|
||||||
}
|
}
|
||||||
|
if (CorsUtils.isPreFlightRequest(exchange.getRequest())) {
|
||||||
|
return handlePreFlight(exchange);
|
||||||
|
}
|
||||||
return Flux.fromIterable(this.handlerMappings)
|
return Flux.fromIterable(this.handlerMappings)
|
||||||
.concatMap(mapping -> mapping.getHandler(exchange))
|
.concatMap(mapping -> mapping.getHandler(exchange))
|
||||||
.next()
|
.next()
|
||||||
|
@ -158,11 +162,8 @@ public class DispatcherHandler implements WebHandler, ApplicationContextAware {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
|
private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
|
||||||
// No handling for CORS rejected requests and pre-flight requests
|
if (ObjectUtils.nullSafeEquals(exchange.getResponse().getStatusCode(), HttpStatus.FORBIDDEN)) {
|
||||||
ServerHttpRequest request = exchange.getRequest();
|
return Mono.empty(); // CORS rejection
|
||||||
HttpStatus status = exchange.getResponse().getStatusCode();
|
|
||||||
if (ObjectUtils.nullSafeEquals(status, HttpStatus.FORBIDDEN) || CorsUtils.isPreFlightRequest(request)) {
|
|
||||||
return Mono.empty();
|
|
||||||
}
|
}
|
||||||
if (this.handlerAdapters != null) {
|
if (this.handlerAdapters != null) {
|
||||||
for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
|
for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
|
||||||
|
@ -196,4 +197,13 @@ public class DispatcherHandler implements WebHandler, ApplicationContextAware {
|
||||||
throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue());
|
throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Void> 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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue