Improve CORS handling
This commit improves CORS support by: - Using CORS processing only for CORS-enabled endpoints - Skipping CORS processing for same-origin requests - Adding Vary headers for non-CORS requests It introduces an AbstractHandlerMapping#hasCorsConfigurationSource method in order to be able to check CORS endpoints efficiently. Closes gh-22273 Closes gh-22496
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -23,6 +23,7 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.context.support.ApplicationObjectSupport;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
@@ -53,6 +54,7 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
|
||||
|
||||
private final PathPatternParser patternParser;
|
||||
|
||||
@Nullable
|
||||
private CorsConfigurationSource corsConfigurationSource;
|
||||
|
||||
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
|
||||
@@ -65,7 +67,6 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
|
||||
|
||||
public AbstractHandlerMapping() {
|
||||
this.patternParser = new PathPatternParser();
|
||||
this.corsConfigurationSource = new UrlBasedCorsConfigurationSource(this.patternParser);
|
||||
}
|
||||
|
||||
|
||||
@@ -113,8 +114,14 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
|
||||
*/
|
||||
public void setCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations) {
|
||||
Assert.notNull(corsConfigurations, "corsConfigurations must not be null");
|
||||
this.corsConfigurationSource = new UrlBasedCorsConfigurationSource(this.patternParser);
|
||||
((UrlBasedCorsConfigurationSource) this.corsConfigurationSource).setCorsConfigurations(corsConfigurations);
|
||||
if (!corsConfigurations.isEmpty()) {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(this.patternParser);
|
||||
source.setCorsConfigurations(corsConfigurations);
|
||||
this.corsConfigurationSource = source;
|
||||
}
|
||||
else {
|
||||
this.corsConfigurationSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,12 +182,12 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);
|
||||
}
|
||||
if (CorsUtils.isCorsRequest(exchange.getRequest())) {
|
||||
CorsConfiguration configA = this.corsConfigurationSource.getCorsConfiguration(exchange);
|
||||
CorsConfiguration configB = getCorsConfiguration(handler, exchange);
|
||||
CorsConfiguration config = (configA != null ? configA.combine(configB) : configB);
|
||||
if (!getCorsProcessor().process(config, exchange) ||
|
||||
CorsUtils.isPreFlightRequest(exchange.getRequest())) {
|
||||
if (hasCorsConfigurationSource(handler)) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(exchange) : null);
|
||||
CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange);
|
||||
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
|
||||
if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {
|
||||
return REQUEST_HANDLED_HANDLER;
|
||||
}
|
||||
}
|
||||
@@ -200,6 +207,14 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
|
||||
*/
|
||||
protected abstract Mono<?> getHandlerInternal(ServerWebExchange exchange);
|
||||
|
||||
/**
|
||||
* Return {@code true} if there is a {@link CorsConfigurationSource} for this handler.
|
||||
* @since 5.2
|
||||
*/
|
||||
protected boolean hasCorsConfigurationSource(Object handler) {
|
||||
return handler instanceof CorsConfigurationSource || this.corsConfigurationSource != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the CORS configuration for the given handler.
|
||||
* @param handler the handler to check (never {@code null})
|
||||
|
||||
@@ -370,6 +370,13 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasCorsConfigurationSource(Object handler) {
|
||||
return super.hasCorsConfigurationSource(handler) ||
|
||||
(handler instanceof HandlerMethod && this.mappingRegistry.getCorsConfiguration((HandlerMethod) handler) != null) ||
|
||||
handler.equals(PREFLIGHT_AMBIGUOUS_MATCH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CorsConfiguration getCorsConfiguration(Object handler, ServerWebExchange exchange) {
|
||||
CorsConfiguration corsConfig = super.getCorsConfiguration(handler, exchange);
|
||||
@@ -451,6 +458,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
/**
|
||||
* Return CORS configuration. Thread-safe for concurrent use.
|
||||
*/
|
||||
@Nullable
|
||||
public CorsConfiguration getCorsConfiguration(HandlerMethod handlerMethod) {
|
||||
HandlerMethod original = handlerMethod.getResolvedFromHandlerMethod();
|
||||
return this.corsLookup.get(original != null ? original : handlerMethod);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -31,7 +31,6 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
/**
|
||||
@@ -74,8 +73,7 @@ public class CorsUrlHandlerMappingTests {
|
||||
Object actual = this.handlerMapping.getHandler(exchange).block();
|
||||
|
||||
assertNotNull(actual);
|
||||
assertNotSame(this.welcomeController, actual);
|
||||
assertNull(exchange.getResponse().getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
|
||||
assertSame(this.welcomeController, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user