EndpointRequest should consider server.servlet.path
Fixes gh-12934
This commit is contained in:
committed by
Phillip Webb
parent
5b3cb8a698
commit
6855c5556d
@@ -33,6 +33,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider;
|
||||
import org.springframework.boot.security.servlet.ApplicationContextRequestMatcher;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
@@ -40,6 +41,7 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* Factory that can be used to create a {@link RequestMatcher} for actuator endpoint
|
||||
@@ -114,7 +116,7 @@ public final class EndpointRequest {
|
||||
* The request matcher used to match against {@link Endpoint actuator endpoints}.
|
||||
*/
|
||||
public static final class EndpointRequestMatcher
|
||||
extends ApplicationContextRequestMatcher<PathMappedEndpoints> {
|
||||
extends ApplicationContextRequestMatcher<WebApplicationContext> {
|
||||
|
||||
private final List<Object> includes;
|
||||
|
||||
@@ -140,7 +142,7 @@ public final class EndpointRequest {
|
||||
|
||||
private EndpointRequestMatcher(List<Object> includes, List<Object> excludes,
|
||||
boolean includeLinks) {
|
||||
super(PathMappedEndpoints.class);
|
||||
super(WebApplicationContext.class);
|
||||
this.includes = includes;
|
||||
this.excludes = excludes;
|
||||
this.includeLinks = includeLinks;
|
||||
@@ -163,32 +165,35 @@ public final class EndpointRequest {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialized(Supplier<PathMappedEndpoints> pathMappedEndpoints) {
|
||||
this.delegate = createDelegate(pathMappedEndpoints);
|
||||
protected void initialized(Supplier<WebApplicationContext> webApplicationContext) {
|
||||
this.delegate = createDelegate(webApplicationContext);
|
||||
}
|
||||
|
||||
private RequestMatcher createDelegate(
|
||||
Supplier<PathMappedEndpoints> pathMappedEndpoints) {
|
||||
Supplier<WebApplicationContext> webApplicationContext) {
|
||||
try {
|
||||
return createDelegate(pathMappedEndpoints.get());
|
||||
WebApplicationContext context = webApplicationContext.get();
|
||||
PathMappedEndpoints pathMappedEndpoints = context.getBean(PathMappedEndpoints.class);
|
||||
DispatcherServletPathProvider pathProvider = context.getBean(DispatcherServletPathProvider.class);
|
||||
return createDelegate(pathMappedEndpoints, pathProvider.getServletPath());
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return EMPTY_MATCHER;
|
||||
}
|
||||
}
|
||||
|
||||
private RequestMatcher createDelegate(PathMappedEndpoints pathMappedEndpoints) {
|
||||
private RequestMatcher createDelegate(PathMappedEndpoints pathMappedEndpoints, String servletPath) {
|
||||
Set<String> paths = new LinkedHashSet<>();
|
||||
if (this.includes.isEmpty()) {
|
||||
paths.addAll(pathMappedEndpoints.getAllPaths());
|
||||
}
|
||||
streamPaths(this.includes, pathMappedEndpoints).forEach(paths::add);
|
||||
streamPaths(this.excludes, pathMappedEndpoints).forEach(paths::remove);
|
||||
List<RequestMatcher> delegateMatchers = getDelegateMatchers(paths);
|
||||
List<RequestMatcher> delegateMatchers = getDelegateMatchers(servletPath, paths);
|
||||
if (this.includeLinks
|
||||
&& StringUtils.hasText(pathMappedEndpoints.getBasePath())) {
|
||||
delegateMatchers.add(
|
||||
new AntPathRequestMatcher(pathMappedEndpoints.getBasePath()));
|
||||
new AntPathRequestMatcher(servletPath + pathMappedEndpoints.getBasePath()));
|
||||
}
|
||||
return new OrRequestMatcher(delegateMatchers);
|
||||
}
|
||||
@@ -216,14 +221,14 @@ public final class EndpointRequest {
|
||||
return annotation.id();
|
||||
}
|
||||
|
||||
private List<RequestMatcher> getDelegateMatchers(Set<String> paths) {
|
||||
return paths.stream().map((path) -> new AntPathRequestMatcher(path + "/**"))
|
||||
private List<RequestMatcher> getDelegateMatchers(String servletPath, Set<String> paths) {
|
||||
return paths.stream().map((path) -> new AntPathRequestMatcher(servletPath + path + "/**"))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(HttpServletRequest request,
|
||||
Supplier<PathMappedEndpoints> context) {
|
||||
Supplier<WebApplicationContext> context) {
|
||||
return this.delegate.matches(request);
|
||||
}
|
||||
|
||||
@@ -233,29 +238,37 @@ public final class EndpointRequest {
|
||||
* The request matcher used to match against the links endpoint.
|
||||
*/
|
||||
public static final class LinksRequestMatcher
|
||||
extends ApplicationContextRequestMatcher<WebEndpointProperties> {
|
||||
extends ApplicationContextRequestMatcher<WebApplicationContext> {
|
||||
|
||||
private RequestMatcher delegate;
|
||||
|
||||
private LinksRequestMatcher() {
|
||||
super(WebEndpointProperties.class);
|
||||
super(WebApplicationContext.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialized(Supplier<WebEndpointProperties> properties) {
|
||||
this.delegate = createDelegate(properties.get());
|
||||
protected void initialized(Supplier<WebApplicationContext> webApplicationContext) {
|
||||
try {
|
||||
WebApplicationContext context = webApplicationContext.get();
|
||||
WebEndpointProperties properties = context.getBean(WebEndpointProperties.class);
|
||||
DispatcherServletPathProvider pathProvider = context.getBean(DispatcherServletPathProvider.class);
|
||||
this.delegate = createDelegate(pathProvider.getServletPath(), properties);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
this.delegate = EMPTY_MATCHER;
|
||||
}
|
||||
}
|
||||
|
||||
private RequestMatcher createDelegate(WebEndpointProperties properties) {
|
||||
private RequestMatcher createDelegate(String path, WebEndpointProperties properties) {
|
||||
if (StringUtils.hasText(properties.getBasePath())) {
|
||||
return new AntPathRequestMatcher(properties.getBasePath());
|
||||
return new AntPathRequestMatcher(path + properties.getBasePath());
|
||||
}
|
||||
return EMPTY_MATCHER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(HttpServletRequest request,
|
||||
Supplier<WebEndpointProperties> context) {
|
||||
Supplier<WebApplicationContext> context) {
|
||||
return this.delegate.matches(request);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider;
|
||||
import org.springframework.boot.web.servlet.error.ErrorAttributes;
|
||||
import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -92,4 +93,9 @@ class WebMvcEndpointChildContextConfiguration {
|
||||
return new OrderedRequestContextFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DispatcherServletPathProvider childDispatcherServletPathProvider() {
|
||||
return () -> "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.boot.actuate.endpoint.Operation;
|
||||
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
@@ -71,6 +72,16 @@ public class EndpointRequestTests {
|
||||
assertMatcher(matcher).doesNotMatch("/actuator/baz");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toAnyEndpointWhenServletPathNotEmptyShouldMatch() {
|
||||
RequestMatcher matcher = EndpointRequest.toAnyEndpoint();
|
||||
assertMatcher(matcher, "/actuator", "/spring").matches("/spring", "/actuator/foo");
|
||||
assertMatcher(matcher, "/actuator", "/spring").matches("/spring", "/actuator/bar");
|
||||
assertMatcher(matcher, "/actuator", "/spring").matches("/spring", "/actuator");
|
||||
assertMatcher(matcher, "/actuator", "/spring").doesNotMatch("/spring", "/actuator/baz");
|
||||
assertMatcher(matcher, "/actuator", "/spring").doesNotMatch("", "/actuator/foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toEndpointClassShouldMatchEndpointPath() {
|
||||
RequestMatcher matcher = EndpointRequest.to(FooEndpoint.class);
|
||||
@@ -114,6 +125,13 @@ public class EndpointRequestTests {
|
||||
assertMatcher.doesNotMatch("/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toLinksWhenServletPathNotEmptyShouldNotMatch() {
|
||||
RequestMatcher matcher = EndpointRequest.toLinks();
|
||||
RequestMatcherAssert assertMatcher = assertMatcher(matcher, "/actuator", "/spring");
|
||||
assertMatcher.matches("/spring/actuator");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void excludeByClassShouldNotMatchExcluded() {
|
||||
RequestMatcher matcher = EndpointRequest.toAnyEndpoint()
|
||||
@@ -179,6 +197,10 @@ public class EndpointRequestTests {
|
||||
return assertMatcher(matcher, mockPathMappedEndpoints(basePath));
|
||||
}
|
||||
|
||||
private RequestMatcherAssert assertMatcher(RequestMatcher matcher, String basePath, String servletPath) {
|
||||
return assertMatcher(matcher, mockPathMappedEndpoints(basePath), servletPath);
|
||||
}
|
||||
|
||||
private PathMappedEndpoints mockPathMappedEndpoints(String basePath) {
|
||||
List<ExposableEndpoint<?>> endpoints = new ArrayList<>();
|
||||
endpoints.add(mockEndpoint("foo", "foo"));
|
||||
@@ -195,6 +217,11 @@ public class EndpointRequestTests {
|
||||
|
||||
private RequestMatcherAssert assertMatcher(RequestMatcher matcher,
|
||||
PathMappedEndpoints pathMappedEndpoints) {
|
||||
return assertMatcher(matcher, pathMappedEndpoints, "");
|
||||
}
|
||||
|
||||
private RequestMatcherAssert assertMatcher(RequestMatcher matcher,
|
||||
PathMappedEndpoints pathMappedEndpoints, String servletPath) {
|
||||
StaticWebApplicationContext context = new StaticWebApplicationContext();
|
||||
context.registerBean(WebEndpointProperties.class);
|
||||
if (pathMappedEndpoints != null) {
|
||||
@@ -205,6 +232,7 @@ public class EndpointRequestTests {
|
||||
properties.setBasePath(pathMappedEndpoints.getBasePath());
|
||||
}
|
||||
}
|
||||
context.registerBean(DispatcherServletPathProvider.class, () -> () -> servletPath);
|
||||
return assertThat(new RequestMatcherAssert(context, matcher));
|
||||
}
|
||||
|
||||
@@ -219,8 +247,12 @@ public class EndpointRequestTests {
|
||||
this.matcher = matcher;
|
||||
}
|
||||
|
||||
public void matches(String path) {
|
||||
matches(mockRequest(path));
|
||||
public void matches(String servletPath) {
|
||||
matches(mockRequest(servletPath));
|
||||
}
|
||||
|
||||
public void matches(String servletPath, String pathInfo) {
|
||||
matches(mockRequest(servletPath, pathInfo));
|
||||
}
|
||||
|
||||
private void matches(HttpServletRequest request) {
|
||||
@@ -228,8 +260,12 @@ public class EndpointRequestTests {
|
||||
.as("Matches " + getRequestPath(request)).isTrue();
|
||||
}
|
||||
|
||||
public void doesNotMatch(String path) {
|
||||
doesNotMatch(mockRequest(path));
|
||||
public void doesNotMatch(String servletPath) {
|
||||
doesNotMatch(mockRequest(servletPath));
|
||||
}
|
||||
|
||||
public void doesNotMatch(String servletPath, String pathInfo) {
|
||||
doesNotMatch(mockRequest(servletPath, pathInfo));
|
||||
}
|
||||
|
||||
private void doesNotMatch(HttpServletRequest request) {
|
||||
@@ -237,8 +273,8 @@ public class EndpointRequestTests {
|
||||
.as("Does not match " + getRequestPath(request)).isFalse();
|
||||
}
|
||||
|
||||
private MockHttpServletRequest mockRequest(String path) {
|
||||
return mockRequest(null, path);
|
||||
private MockHttpServletRequest mockRequest(String servletPath) {
|
||||
return mockRequest(servletPath, null);
|
||||
}
|
||||
|
||||
private MockHttpServletRequest mockRequest(String servletPath, String path) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.boot.actuate.autoconfigure.web.servlet;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider;
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -62,6 +63,13 @@ public class WebMvcEndpointChildContextConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextShouldConfigureDispatcherServletPathProviderWithEmptyPath() {
|
||||
this.contextRunner.withUserConfiguration(WebMvcEndpointChildContextConfiguration.class)
|
||||
.run((context) -> assertThat(context.getBean(DispatcherServletPathProvider.class)
|
||||
.getServletPath()).isEmpty());
|
||||
}
|
||||
|
||||
static class ExistingConfig {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -88,8 +88,11 @@ public class DispatcherServletAutoConfiguration {
|
||||
|
||||
private final WebMvcProperties webMvcProperties;
|
||||
|
||||
public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) {
|
||||
private final ServerProperties serverProperties;
|
||||
|
||||
public DispatcherServletConfiguration(WebMvcProperties webMvcProperties, ServerProperties serverProperties) {
|
||||
this.webMvcProperties = webMvcProperties;
|
||||
this.serverProperties = serverProperties;
|
||||
}
|
||||
|
||||
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
|
||||
@@ -112,6 +115,11 @@ public class DispatcherServletAutoConfiguration {
|
||||
return resolver;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DispatcherServletPathProvider mainDispatcherServletPathProvider() {
|
||||
return () -> DispatcherServletConfiguration.this.serverProperties.getServlet().getPath();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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
|
||||
*
|
||||
* http://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.boot.autoconfigure.web.servlet;
|
||||
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
/**
|
||||
* Interface that provides the path of the {@link DispatcherServlet} in
|
||||
* an application context.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.0.2
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DispatcherServletPathProvider {
|
||||
|
||||
String getServletPath();
|
||||
|
||||
}
|
||||
|
||||
@@ -107,6 +107,8 @@ public class DispatcherServletAutoConfigurationTests {
|
||||
assertThat(registration.getUrlMappings().toString())
|
||||
.isEqualTo("[/spring/*]");
|
||||
assertThat(registration.getMultipartConfig()).isNull();
|
||||
assertThat(context.getBean(DispatcherServletPathProvider.class)
|
||||
.getServletPath()).isEqualTo("/spring");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user