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
This commit is contained in:
Stephane Nicoll 2014-12-12 11:53:26 +01:00
parent f8141cbb95
commit e96f75fdc1
1 changed files with 67 additions and 34 deletions

View File

@ -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<MvcEndpoint> set = new HashSet<MvcEndpoint>(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<EndpointHandlerMappingCustomizer> 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<MvcEndpoint> set = new HashSet<MvcEndpoint>(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)