diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java index 20c841538ef..138e39f0d96 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java @@ -44,12 +44,11 @@ class PathResourceLookupFunction implements Function apply(ServerRequest request) { PathContainer pathContainer = request.requestPath().pathWithinApplication(); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceHandlerUtils.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceHandlerUtils.java index 2e5a1892343..9de12816cf5 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceHandlerUtils.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceHandlerUtils.java @@ -27,6 +27,8 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.core.log.LogFormatUtils; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; import org.springframework.web.util.UriUtils; @@ -42,6 +44,42 @@ public abstract class ResourceHandlerUtils { private static final Log logger = LogFactory.getLog(ResourceHandlerUtils.class); + private static final String FOLDER_SEPARATOR = "/"; + + private static final String WINDOWS_FOLDER_SEPARATOR = "\\"; + + + /** + * Assert the given location is not null, and its path ends on slash. + */ + public static void assertResourceLocation(@Nullable Resource location) { + Assert.notNull(location, "Resource location must not be null"); + try { + String path; + if (location instanceof UrlResource) { + path = location.getURL().toExternalForm(); + } + else if (location instanceof ClassPathResource classPathResource) { + path = classPathResource.getPath(); + } + else { + path = location.getURL().getPath(); + } + assertLocationPath(path); + } + catch (IOException ex) { + // ignore + } + } + + /** + * Assert the given location path is a directory and ends on slash. + */ + public static void assertLocationPath(@Nullable String path) { + Assert.notNull(path, "Resource location path must not be null"); + Assert.isTrue(path.endsWith(FOLDER_SEPARATOR) || path.endsWith(WINDOWS_FOLDER_SEPARATOR), + "Resource location does not end with slash: " + path); + } /** * Normalize the given resource path replacing the following: diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java index ef30d162356..eb233f00e01 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java @@ -160,7 +160,10 @@ public class ResourceWebHandler implements WebHandler, InitializingBean { public void setLocations(@Nullable List locations) { this.locationResources.clear(); if (locations != null) { - this.locationResources.addAll(locations); + for (Resource location : locations) { + ResourceHandlerUtils.assertResourceLocation(location); + this.locationResources.add(location); + } } } @@ -376,6 +379,7 @@ public class ResourceWebHandler implements WebHandler, InitializingBean { Assert.isTrue(CollectionUtils.isEmpty(this.locationResources), "Please set " + "either Resource-based \"locations\" or String-based \"locationValues\", but not both."); for (String location : this.locationValues) { + ResourceHandlerUtils.assertLocationPath(location); result.add(this.resourceLoader.getResource(location)); } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java index df78c060926..3262f36c03d 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/config/DelegatingWebFluxConfigurationTests.java @@ -124,7 +124,7 @@ public class DelegatingWebFluxConfigurationTests { delegatingConfig.setConfigurers(Collections.singletonList(webFluxConfigurer)); willAnswer(invocation -> { ResourceHandlerRegistry registry = invocation.getArgument(0); - registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static"); + registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); return null; }).given(webFluxConfigurer).addResourceHandlers(any(ResourceHandlerRegistry.class)); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java index 80dd6212750..8b02cce63a5 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java @@ -47,7 +47,7 @@ class PathResourceLookupFunction implements Function locations) { Assert.notNull(locations, "Locations list must not be null"); this.locationResources.clear(); - this.locationResources.addAll(locations); + for (Resource location : locations) { + ResourceHandlerUtils.assertResourceLocation(location); + this.locationResources.add(location); + } } /** @@ -493,6 +496,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator charset = Charset.forName(value); location = location.substring(endIndex + 1); } + ResourceHandlerUtils.assertLocationPath(location); Resource resource = applicationContext.getResource(location); if (location.equals("/") && !(resource instanceof ServletContextResource)) { throw new IllegalStateException( diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java index 0cadc15f58d..ae7a811357b 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java @@ -204,7 +204,7 @@ class ResourceHandlerRegistryTests { @Test void urlResourceWithCharset() { - this.registration.addResourceLocations("[charset=ISO-8859-1]file:///tmp"); + this.registration.addResourceLocations("[charset=ISO-8859-1]file:///tmp/"); this.registration.resourceChain(true); ResourceHttpRequestHandler handler = getHandler("/resources/**"); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java index 13738ff588f..6c3f801fb85 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportExtensionTests.java @@ -457,7 +457,7 @@ class WebMvcConfigurationSupportExtensionTests { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("/resources/**").addResourceLocations("src/test/java"); + registry.addResourceHandler("/resources/**").addResourceLocations("src/test/java/"); } @Override diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources-chain.xml b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources-chain.xml index 5ab96045b80..6ab67c7449f 100644 --- a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources-chain.xml +++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/mvc-config-resources-chain.xml @@ -23,7 +23,7 @@ - location=file:///tmp + location=file:///tmp/