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.
|
||||
@@ -81,7 +81,8 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
|
||||
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
|
||||
|
||||
private CorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
|
||||
@Nullable
|
||||
private CorsConfigurationSource corsConfigurationSource;
|
||||
|
||||
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
|
||||
|
||||
@@ -206,11 +207,16 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
*/
|
||||
public void setCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations) {
|
||||
Assert.notNull(corsConfigurations, "corsConfigurations must not be null");
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.setCorsConfigurations(corsConfigurations);
|
||||
source.setPathMatcher(this.pathMatcher);
|
||||
source.setUrlPathHelper(this.urlPathHelper);
|
||||
this.corsConfigurationSource = source;
|
||||
if (!corsConfigurations.isEmpty()) {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.setCorsConfigurations(corsConfigurations);
|
||||
source.setPathMatcher(this.pathMatcher);
|
||||
source.setUrlPathHelper(this.urlPathHelper);
|
||||
this.corsConfigurationSource = source;
|
||||
}
|
||||
else {
|
||||
this.corsConfigurationSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -420,10 +426,10 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
logger.debug("Mapped to " + executionChain.getHandler());
|
||||
}
|
||||
|
||||
if (CorsUtils.isCorsRequest(request)) {
|
||||
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
|
||||
if (hasCorsConfigurationSource(handler)) {
|
||||
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
|
||||
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
|
||||
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
|
||||
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
|
||||
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
|
||||
}
|
||||
|
||||
@@ -488,6 +494,14 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
|
||||
return chain;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}).
|
||||
|
||||
@@ -448,6 +448,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, HttpServletRequest request) {
|
||||
CorsConfiguration corsConfig = super.getCorsConfiguration(handler, request);
|
||||
@@ -555,6 +562,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.
|
||||
@@ -138,7 +138,7 @@ public class WebMvcConfigurationSupportExtensionTests {
|
||||
HandlerExecutionChain chain = rmHandlerMapping.getHandler(new MockHttpServletRequest("GET", "/"));
|
||||
assertNotNull(chain);
|
||||
assertNotNull(chain.getInterceptors());
|
||||
assertEquals(3, chain.getInterceptors().length);
|
||||
assertEquals(4, chain.getInterceptors().length);
|
||||
assertEquals(LocaleChangeInterceptor.class, chain.getInterceptors()[0].getClass());
|
||||
assertEquals(ConversionServiceExposingInterceptor.class, chain.getInterceptors()[1].getClass());
|
||||
assertEquals(ResourceUrlProviderExposingInterceptor.class, chain.getInterceptors()[2].getClass());
|
||||
@@ -177,7 +177,7 @@ public class WebMvcConfigurationSupportExtensionTests {
|
||||
chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/resources/foo.gif"));
|
||||
assertNotNull(chain);
|
||||
assertNotNull(chain.getHandler());
|
||||
assertEquals(Arrays.toString(chain.getInterceptors()), 4, chain.getInterceptors().length);
|
||||
assertEquals(Arrays.toString(chain.getInterceptors()), 5, chain.getInterceptors().length);
|
||||
// PathExposingHandlerInterceptor at chain.getInterceptors()[0]
|
||||
assertEquals(LocaleChangeInterceptor.class, chain.getInterceptors()[1].getClass());
|
||||
assertEquals(ConversionServiceExposingInterceptor.class, chain.getInterceptors()[2].getClass());
|
||||
|
||||
@@ -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.
|
||||
@@ -81,8 +81,7 @@ public class CorsAbstractHandlerMappingTests {
|
||||
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
|
||||
HandlerExecutionChain chain = handlerMapping.getHandler(this.request);
|
||||
assertNotNull(chain);
|
||||
assertNotNull(chain.getHandler());
|
||||
assertTrue(chain.getHandler().getClass().getSimpleName().equals("PreFlightHandler"));
|
||||
assertTrue(chain.getHandler() instanceof SimpleHandler);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user