From e96f75fdc1053da344348f7be8d32130b24d21c2 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 12 Dec 2014 11:53:26 +0100 Subject: [PATCH] Fix management endpoint without Spring Security The method 'injectIntoSecurityFilter' added In 3c1e48c assumes that Spring security is in the classpath so any management endpoints that are deployed on a different port requires Spring Security all the sudden. This commit separates the creating of the EndpointHandlerMapping in two mutually exclusive @Configuration: one that is triggered if Spring Security is not in the classpath and one that is triggered if Spring Security is in the classpath. The latter apply the security filter in the endpoint mapping if it exists. Fixes gh-2124 --- ...dpointWebMvcChildContextConfiguration.java | 101 ++++++++++++------ 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java index 16d42130c14..13fdbe6d39b 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcChildContextConfiguration.java @@ -39,6 +39,7 @@ import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.web.ErrorAttributes; import org.springframework.boot.autoconfigure.web.HttpMessageConverters; @@ -50,6 +51,7 @@ import org.springframework.boot.context.embedded.ErrorPage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.HandlerAdapter; import org.springframework.web.servlet.HandlerMapping; @@ -133,40 +135,6 @@ public class EndpointWebMvcChildContextConfiguration { return adapter; } - @Bean - public HandlerMapping handlerMapping(MvcEndpoints endpoints, - ListableBeanFactory beanFactory) { - Set set = new HashSet(endpoints.getEndpoints()); - set.addAll(beanFactory.getBeansOfType(MvcEndpoint.class).values()); - EndpointHandlerMapping mapping = new EndpointHandlerMapping(set); - // In a child context we definitely want to see the parent endpoints - mapping.setDetectHandlerMethodsInAncestorContexts(true); - injectIntoSecurityFilter(beanFactory, mapping); - if (this.mappingCustomizers != null) { - for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) { - customizer.customize(mapping); - } - } - return mapping; - } - - private void injectIntoSecurityFilter(ListableBeanFactory beanFactory, - EndpointHandlerMapping mapping) { - // The parent context has the security filter, so we need to get it injected with - // our EndpointHandlerMapping if we can. - if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, - ManagementWebSecurityConfigurerAdapter.class).length == 1) { - ManagementWebSecurityConfigurerAdapter bean = beanFactory - .getBean(ManagementWebSecurityConfigurerAdapter.class); - bean.setEndpointHandlerMapping(mapping); - } - else { - logger.warn("No single bean of type " - + ManagementWebSecurityConfigurerAdapter.class.getSimpleName() - + " found (this might make some endpoints inaccessible without authentication)"); - } - } - /* * The error controller is present but not mapped as an endpoint in this context * because of the DispatcherServlet having had it's HandlerMapping explicitly @@ -177,6 +145,71 @@ public class EndpointWebMvcChildContextConfiguration { return new ManagementErrorEndpoint(this.errorPath, errorAttributes); } + @Configuration + @ConditionalOnMissingClass(WebSecurityConfigurerAdapter.class) + static class EndpointHandlerMappingSimpleConfiguration { + + @Autowired(required = false) + private List mappingCustomizers; + + @Bean + public HandlerMapping handlerMapping(MvcEndpoints endpoints, + ListableBeanFactory beanFactory) { + + EndpointHandlerMapping mapping = doCreateEndpointHandlerMapping(endpoints, beanFactory); + if (this.mappingCustomizers != null) { + for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) { + customizer.customize(mapping); + } + } + return mapping; + } + + protected EndpointHandlerMapping doCreateEndpointHandlerMapping(MvcEndpoints endpoints, + ListableBeanFactory beanFactory) { + Set set = new HashSet(endpoints.getEndpoints()); + set.addAll(beanFactory.getBeansOfType(MvcEndpoint.class).values()); + EndpointHandlerMapping mapping = new EndpointHandlerMapping(set); + // In a child context we definitely want to see the parent endpoints + mapping.setDetectHandlerMethodsInAncestorContexts(true); + return mapping; + } + + } + + @Configuration + @ConditionalOnClass(WebSecurityConfigurerAdapter.class) + static class EndpointHandlerMappingSecurityConfiguration + extends EndpointHandlerMappingSimpleConfiguration { + + @Override + protected EndpointHandlerMapping doCreateEndpointHandlerMapping(MvcEndpoints endpoints, + ListableBeanFactory beanFactory) { + + EndpointHandlerMapping mapping = super.doCreateEndpointHandlerMapping(endpoints, beanFactory); + injectIntoSecurityFilter(beanFactory, mapping); + return mapping; + } + + private void injectIntoSecurityFilter(ListableBeanFactory beanFactory, + EndpointHandlerMapping mapping) { + // The parent context has the security filter, so we need to get it injected with + // our EndpointHandlerMapping if we can. + if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, + ManagementWebSecurityConfigurerAdapter.class).length == 1) { + ManagementWebSecurityConfigurerAdapter bean = beanFactory + .getBean(ManagementWebSecurityConfigurerAdapter.class); + bean.setEndpointHandlerMapping(mapping); + } + else { + logger.warn("No single bean of type " + + ManagementWebSecurityConfigurerAdapter.class.getSimpleName() + + " found (this might make some endpoints inaccessible without authentication)"); + } + } + + } + @Configuration @ConditionalOnClass({ EnableWebSecurity.class, Filter.class }) @ConditionalOnBean(name = "springSecurityFilterChain", search = SearchStrategy.PARENTS)