Introduce CorsFilter and CorsConfigurationMapping
This commit introduces the following changes: - The new CorsConfigurationMapping class allows to share the mapped CorsConfiguration logic between AbstractHandlerMapping and CorsFilter - In AbstractHandlerMapping, the Map<String, CorsConfiguration> corsConfiguration property has been renamed to corsConfigurations - CorsFilter allows to process CORS requests at filter level, using any CorsConfigurationSource implementation (for example CorsConfigurationMapping) Issue: SPR-13192
This commit is contained in:
@@ -197,8 +197,8 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
configurePathMatchingProperties(handlerMappingDef, element, parserContext);
|
||||
|
||||
RuntimeBeanReference corsConfigurationRef = MvcNamespaceUtils.registerCorsConfiguration(null, parserContext, source);
|
||||
handlerMappingDef.getPropertyValues().add("corsConfiguration", corsConfigurationRef);
|
||||
RuntimeBeanReference corsConfigurationsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
|
||||
handlerMappingDef.getPropertyValues().add("corsConfigurations", corsConfigurationsRef);
|
||||
|
||||
RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
|
||||
RuntimeBeanReference validator = getValidator(element, source, parserContext);
|
||||
|
||||
@@ -113,7 +113,7 @@ public class CorsBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
}
|
||||
|
||||
MvcNamespaceUtils.registerCorsConfiguration(corsConfigurations, parserContext, parserContext.extractSource(element));
|
||||
MvcNamespaceUtils.registerCorsConfigurations(corsConfigurations, parserContext, parserContext.extractSource(element));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,8 +119,8 @@ abstract class MvcNamespaceUtils {
|
||||
beanNameMappingDef.setSource(source);
|
||||
beanNameMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
beanNameMappingDef.getPropertyValues().add("order", 2); // consistent with WebMvcConfigurationSupport
|
||||
RuntimeBeanReference corsConfigurationRef = MvcNamespaceUtils.registerCorsConfiguration(null, parserContext, source);
|
||||
beanNameMappingDef.getPropertyValues().add("corsConfiguration", corsConfigurationRef);
|
||||
RuntimeBeanReference corsConfigurationsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
|
||||
beanNameMappingDef.getPropertyValues().add("corsConfigurations", corsConfigurationsRef);
|
||||
parserContext.getRegistry().registerBeanDefinition(BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME, beanNameMappingDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(beanNameMappingDef, BEAN_NAME_URL_HANDLER_MAPPING_BEAN_NAME));
|
||||
}
|
||||
@@ -160,20 +160,20 @@ abstract class MvcNamespaceUtils {
|
||||
* if a non-null CORS configuration is provided.
|
||||
* @return a RuntimeBeanReference to this {@code Map<String, CorsConfiguration>} instance
|
||||
*/
|
||||
public static RuntimeBeanReference registerCorsConfiguration(Map<String, CorsConfiguration> corsConfiguration, ParserContext parserContext, Object source) {
|
||||
public static RuntimeBeanReference registerCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations, ParserContext parserContext, Object source) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(CORS_CONFIGURATION_BEAN_NAME)) {
|
||||
RootBeanDefinition corsConfigurationsDef = new RootBeanDefinition(LinkedHashMap.class);
|
||||
corsConfigurationsDef.setSource(source);
|
||||
corsConfigurationsDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
if (corsConfiguration != null) {
|
||||
corsConfigurationsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfiguration);
|
||||
if (corsConfigurations != null) {
|
||||
corsConfigurationsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfigurations);
|
||||
}
|
||||
parserContext.getReaderContext().getRegistry().registerBeanDefinition(CORS_CONFIGURATION_BEAN_NAME, corsConfigurationsDef);
|
||||
parserContext.registerComponent(new BeanComponentDefinition(corsConfigurationsDef, CORS_CONFIGURATION_BEAN_NAME));
|
||||
}
|
||||
else if (corsConfiguration != null) {
|
||||
else if (corsConfigurations != null) {
|
||||
BeanDefinition corsConfigurationsDef = parserContext.getRegistry().getBeanDefinition(CORS_CONFIGURATION_BEAN_NAME);
|
||||
corsConfigurationsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfiguration);
|
||||
corsConfigurationsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfigurations);
|
||||
}
|
||||
return new RuntimeBeanReference(CORS_CONFIGURATION_BEAN_NAME);
|
||||
}
|
||||
|
||||
@@ -116,8 +116,8 @@ class ResourcesBeanDefinitionParser implements BeanDefinitionParser {
|
||||
// Use a default of near-lowest precedence, still allowing for even lower precedence in other mappings
|
||||
handlerMappingDef.getPropertyValues().add("order", StringUtils.hasText(order) ? order : Ordered.LOWEST_PRECEDENCE - 1);
|
||||
|
||||
RuntimeBeanReference corsConfigurationRef = MvcNamespaceUtils.registerCorsConfiguration(null, parserContext, source);
|
||||
handlerMappingDef.getPropertyValues().add("corsConfiguration", corsConfigurationRef);
|
||||
RuntimeBeanReference corsConfigurationsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
|
||||
handlerMappingDef.getPropertyValues().add("corsConfigurations", corsConfigurationsRef);
|
||||
|
||||
String beanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
|
||||
parserContext.getRegistry().registerBeanDefinition(beanName, handlerMappingDef);
|
||||
|
||||
@@ -127,8 +127,8 @@ class ViewControllerBeanDefinitionParser implements BeanDefinitionParser {
|
||||
beanDef.getPropertyValues().add("order", "1");
|
||||
beanDef.getPropertyValues().add("pathMatcher", MvcNamespaceUtils.registerPathMatcher(null, context, source));
|
||||
beanDef.getPropertyValues().add("urlPathHelper", MvcNamespaceUtils.registerUrlPathHelper(null, context, source));
|
||||
RuntimeBeanReference corsConfigurationRef = MvcNamespaceUtils.registerCorsConfiguration(null, context, source);
|
||||
beanDef.getPropertyValues().add("corsConfiguration", corsConfigurationRef);
|
||||
RuntimeBeanReference corsConfigurationsRef = MvcNamespaceUtils.registerCorsConfigurations(null, context, source);
|
||||
beanDef.getPropertyValues().add("corsConfigurations", corsConfigurationsRef);
|
||||
|
||||
return beanDef;
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
||||
handlerMapping.setOrder(0);
|
||||
handlerMapping.setInterceptors(getInterceptors());
|
||||
handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
|
||||
handlerMapping.setCorsConfiguration(getCorsConfigurations());
|
||||
handlerMapping.setCorsConfigurations(getCorsConfigurations());
|
||||
|
||||
PathMatchConfigurer configurer = getPathMatchConfigurer();
|
||||
if (configurer.isUseSuffixPatternMatch() != null) {
|
||||
@@ -371,7 +371,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
||||
handlerMapping.setPathMatcher(mvcPathMatcher());
|
||||
handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
|
||||
handlerMapping.setInterceptors(getInterceptors());
|
||||
handlerMapping.setCorsConfiguration(getCorsConfigurations());
|
||||
handlerMapping.setCorsConfigurations(getCorsConfigurations());
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
@@ -391,7 +391,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
||||
BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
|
||||
mapping.setOrder(2);
|
||||
mapping.setInterceptors(getInterceptors());
|
||||
mapping.setCorsConfiguration(getCorsConfigurations());
|
||||
mapping.setCorsConfigurations(getCorsConfigurations());
|
||||
return mapping;
|
||||
}
|
||||
|
||||
@@ -411,7 +411,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
||||
handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
|
||||
handlerMapping.setInterceptors(new HandlerInterceptor[] {
|
||||
new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())});
|
||||
handlerMapping.setCorsConfiguration(getCorsConfigurations());
|
||||
handlerMapping.setCorsConfigurations(getCorsConfigurations());
|
||||
}
|
||||
else {
|
||||
handlerMapping = new EmptyHandlerMapping();
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.web.servlet.handler;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -29,6 +28,7 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.HttpRequestHandler;
|
||||
import org.springframework.web.cors.CorsConfigurationMapping;
|
||||
import org.springframework.web.cors.CorsProcessor;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
@@ -81,8 +81,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
|
||||
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
|
||||
|
||||
private final Map<String, CorsConfiguration> corsConfiguration =
|
||||
new LinkedHashMap<String, CorsConfiguration>();
|
||||
private final CorsConfigurationMapping corsMapping = new CorsConfigurationMapping();
|
||||
|
||||
|
||||
/**
|
||||
@@ -125,6 +124,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
*/
|
||||
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
|
||||
this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
|
||||
this.corsMapping.setAlwaysUseFullPath(alwaysUseFullPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,6 +136,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
*/
|
||||
public void setUrlDecode(boolean urlDecode) {
|
||||
this.urlPathHelper.setUrlDecode(urlDecode);
|
||||
this.corsMapping.setUrlDecode(urlDecode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,6 +146,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
*/
|
||||
public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
|
||||
this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent);
|
||||
this.corsMapping.setRemoveSemicolonContent(removeSemicolonContent);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,6 +158,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
||||
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
|
||||
this.urlPathHelper = urlPathHelper;
|
||||
this.corsMapping.setUrlPathHelper(urlPathHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,6 +176,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||
Assert.notNull(pathMatcher, "PathMatcher must not be null");
|
||||
this.pathMatcher = pathMatcher;
|
||||
this.corsMapping.setPathMatcher(pathMatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,18 +225,15 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
* handler, if any.
|
||||
* @since 4.2
|
||||
*/
|
||||
public void setCorsConfiguration(Map<String, CorsConfiguration> corsConfiguration) {
|
||||
this.corsConfiguration.clear();
|
||||
if (corsConfiguration != null) {
|
||||
this.corsConfiguration.putAll(corsConfiguration);
|
||||
}
|
||||
public void setCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations) {
|
||||
this.corsMapping.setCorsConfigurations(corsConfigurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CORS configuration.
|
||||
*/
|
||||
public Map<String, CorsConfiguration> getCorsConfiguration() {
|
||||
return this.corsConfiguration;
|
||||
public Map<String, CorsConfiguration> getCorsConfigurations() {
|
||||
return this.corsMapping.getCorsConfigurations();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -361,7 +362,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
}
|
||||
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
|
||||
if (CorsUtils.isCorsRequest(request)) {
|
||||
CorsConfiguration globalConfig = getCorsConfiguration(request);
|
||||
CorsConfiguration globalConfig = this.corsMapping.getCorsConfiguration(request);
|
||||
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
|
||||
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
|
||||
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
|
||||
@@ -378,7 +379,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
* the pre-flight request but for the expected actual request based on the URL
|
||||
* path, the HTTP methods from the "Access-Control-Request-Method" header, and
|
||||
* the headers from the "Access-Control-Request-Headers" header thus allowing
|
||||
* the CORS configuration to be obtained via {@link #getCorsConfiguration},
|
||||
* the CORS configuration to be obtained via {@link #getCorsConfigurations},
|
||||
*
|
||||
* <p>Note: This method may also return a pre-built {@link HandlerExecutionChain},
|
||||
* combining a handler object with dynamically determined interceptors.
|
||||
@@ -429,22 +430,6 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
return chain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the "global" CORS configuration for the given URL configured via
|
||||
* {@link #setCorsConfiguration(Map)}.
|
||||
* @param request the request
|
||||
* @return the CORS configuration or {@code null}
|
||||
*/
|
||||
protected CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
|
||||
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
|
||||
for(Map.Entry<String, CorsConfiguration> entry : getCorsConfiguration().entrySet()) {
|
||||
if (getPathMatcher().match(entry.getKey(), lookupPath)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the CORS configuration for the given handler.
|
||||
* @param handler the handler to check (never {@code null}).
|
||||
|
||||
Reference in New Issue
Block a user