mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-07-15 01:07:30 +08:00
[bs-138] Make it easy to secure only the management endpoints
Example: web UI with publicly available static assets # application.properties: security.ignored: /css/**,/script/** Example: web UI with publicly available everything, but secure management endpoints. # application.properties: # Empty path for basic security (default is /**) security.basic.path= [Fixes #50721675]
This commit is contained in:
parent
7b0ec252dd
commit
e011312c68
@ -168,6 +168,27 @@ a jar which wraps `SpringApplication`:
|
|||||||
|
|
||||||
$ java -jar myproject.jar --spring.config.name=myproject
|
$ java -jar myproject.jar --spring.config.name=myproject
|
||||||
|
|
||||||
|
## Providing Defaults for Externalized Configuration
|
||||||
|
|
||||||
|
For `@ConfigurationProperties` beans that are provided by the
|
||||||
|
framework itself you can always change the values that are bound to it
|
||||||
|
by changing `application.properties`. But it is sometimes also useful
|
||||||
|
to change the default values imperatively in Java, so get more control
|
||||||
|
over the process. You can do this by declaring a bean of the same
|
||||||
|
type in your application context, e.g. for the server properties:
|
||||||
|
|
||||||
|
@AssertMissingBean(ServerProperties.class)
|
||||||
|
@Bean
|
||||||
|
public ServerProperties serverProperties() {
|
||||||
|
ServerProperties server = new ServerProperties();
|
||||||
|
server.setPort(8888);
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
Note the use of `@AssertMissingBean` to guard against any mistakes
|
||||||
|
where the bean is already defined (and therefore might already have
|
||||||
|
been bound).
|
||||||
|
|
||||||
## Server Configuration
|
## Server Configuration
|
||||||
|
|
||||||
The `ServerProperties` are bound to application properties, and
|
The `ServerProperties` are bound to application properties, and
|
||||||
@ -337,9 +358,13 @@ every request in the main server (and the management server if it is
|
|||||||
running on the same port). There is a single account by default, and
|
running on the same port). There is a single account by default, and
|
||||||
you can test it like this:
|
you can test it like this:
|
||||||
|
|
||||||
$ mvn user:password@localhost:8080/info
|
$ mvn user:password@localhost:8080/metrics
|
||||||
... stuff comes out
|
... stuff comes out
|
||||||
|
|
||||||
|
If the management server is running on a different port it is
|
||||||
|
unsecured by default. If you want to secure it you can add a security
|
||||||
|
auto configuration explicitly
|
||||||
|
|
||||||
## Security - HTTPS
|
## Security - HTTPS
|
||||||
|
|
||||||
Ensuring that all your main endpoints are only available over HTTPS is
|
Ensuring that all your main endpoints are only available over HTTPS is
|
||||||
@ -357,10 +382,14 @@ entries to `application.properties`, e.g.
|
|||||||
server.tomcat.remote_ip_header: x-forwarded-for
|
server.tomcat.remote_ip_header: x-forwarded-for
|
||||||
server.tomcat.protocol_header: x-forwarded-proto
|
server.tomcat.protocol_header: x-forwarded-proto
|
||||||
|
|
||||||
(Or you can add the `RemoteIpValve` yourself by adding a
|
(The presence of either of those properties will switch on the
|
||||||
|
valve. Or you can add the `RemoteIpValve` yourself by adding a
|
||||||
`TomcatEmbeddedServletContainerFactory` bean.)
|
`TomcatEmbeddedServletContainerFactory` bean.)
|
||||||
|
|
||||||
TODO: Spring Security configuration for 'require channel'.
|
Spring Security can also be configured to require a secure channel for
|
||||||
|
all (or some requests). To switch that on in an Actuator application
|
||||||
|
you just need to set `security.require_https: true` in
|
||||||
|
`application.properties`.
|
||||||
|
|
||||||
## Audit Events
|
## Audit Events
|
||||||
|
|
||||||
|
@ -16,12 +16,22 @@
|
|||||||
|
|
||||||
package org.springframework.bootstrap.actuate.autoconfigure;
|
package org.springframework.bootstrap.actuate.autoconfigure;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.HierarchicalBeanFactory;
|
import org.springframework.beans.factory.HierarchicalBeanFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.bootstrap.actuate.endpoint.error.ErrorEndpoint;
|
import org.springframework.bootstrap.actuate.endpoint.error.ErrorEndpoint;
|
||||||
import org.springframework.bootstrap.actuate.properties.ManagementServerProperties;
|
import org.springframework.bootstrap.actuate.properties.ManagementServerProperties;
|
||||||
import org.springframework.bootstrap.context.annotation.ConditionalOnBean;
|
import org.springframework.bootstrap.context.annotation.ConditionalOnBean;
|
||||||
|
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
|
||||||
import org.springframework.bootstrap.context.embedded.ConfigurableEmbeddedServletContainerFactory;
|
import org.springframework.bootstrap.context.embedded.ConfigurableEmbeddedServletContainerFactory;
|
||||||
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerCustomizer;
|
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerCustomizer;
|
||||||
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerFactory;
|
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerFactory;
|
||||||
@ -31,8 +41,10 @@ import org.springframework.bootstrap.context.embedded.tomcat.TomcatEmbeddedServl
|
|||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.filter.GenericFilterBean;
|
||||||
import org.springframework.web.servlet.DispatcherServlet;
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
|
||||||
@ -43,6 +55,7 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
|||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
|
@Import(ManagementSecurityConfiguration.class)
|
||||||
public class ManagementServerConfiguration {
|
public class ManagementServerConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ -100,3 +113,28 @@ public class ManagementServerConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnClass(name = {
|
||||||
|
"org.springframework.security.config.annotation.web.EnableWebSecurity",
|
||||||
|
"javax.servlet.Filter" })
|
||||||
|
class ManagementSecurityConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
// TODO: enable and get rid of the empty filter when @ConditionalOnBean works
|
||||||
|
// @ConditionalOnBean(name = "springSecurityFilterChain")
|
||||||
|
public Filter springSecurityFilterChain(HierarchicalBeanFactory beanFactory) {
|
||||||
|
BeanFactory parent = beanFactory.getParentBeanFactory();
|
||||||
|
if (parent != null && parent.containsBean("springSecurityFilterChain")) {
|
||||||
|
return parent.getBean("springSecurityFilterChain", Filter.class);
|
||||||
|
}
|
||||||
|
return new GenericFilterBean() {
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest request, ServletResponse response,
|
||||||
|
FilterChain chain) throws IOException, ServletException {
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -16,6 +16,11 @@
|
|||||||
|
|
||||||
package org.springframework.bootstrap.actuate.autoconfigure;
|
package org.springframework.bootstrap.actuate.autoconfigure;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.bootstrap.actuate.properties.EndpointsProperties;
|
import org.springframework.bootstrap.actuate.properties.EndpointsProperties;
|
||||||
import org.springframework.bootstrap.actuate.properties.SecurityProperties;
|
import org.springframework.bootstrap.actuate.properties.SecurityProperties;
|
||||||
@ -23,11 +28,16 @@ import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
|
|||||||
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
|
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
|
||||||
import org.springframework.bootstrap.context.annotation.EnableConfigurationProperties;
|
import org.springframework.bootstrap.context.annotation.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Condition;
|
||||||
|
import org.springframework.context.annotation.ConditionContext;
|
||||||
|
import org.springframework.context.annotation.Conditional;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||||
import org.springframework.security.authentication.ProviderManager;
|
import org.springframework.security.authentication.ProviderManager;
|
||||||
|
import org.springframework.security.config.BeanIds;
|
||||||
import org.springframework.security.config.annotation.authentication.AuthenticationManagerBuilder;
|
import org.springframework.security.config.annotation.authentication.AuthenticationManagerBuilder;
|
||||||
import org.springframework.security.config.annotation.web.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.EnableWebSecurity;
|
||||||
import org.springframework.security.config.annotation.web.HttpConfiguration;
|
import org.springframework.security.config.annotation.web.HttpConfiguration;
|
||||||
@ -58,6 +68,7 @@ public class SecurityAutoConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ConditionalOnMissingBean({ BoostrapWebSecurityConfigurerAdapter.class })
|
||||||
public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter() {
|
public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter() {
|
||||||
return new BoostrapWebSecurityConfigurerAdapter();
|
return new BoostrapWebSecurityConfigurerAdapter();
|
||||||
}
|
}
|
||||||
@ -76,20 +87,42 @@ public class SecurityAutoConfiguration {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(HttpConfiguration http) throws Exception {
|
protected void configure(HttpConfiguration http) throws Exception {
|
||||||
|
|
||||||
if (this.security.isRequireSsl()) {
|
if (this.security.isRequireSsl()) {
|
||||||
http.requiresChannel().antMatchers("/**").requiresSecure();
|
http.requiresChannel().anyRequest().requiresSecure();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.security.getBasic().isEnabled()) {
|
if (this.security.getBasic().isEnabled()) {
|
||||||
HttpConfiguration matcher = http.antMatcher(this.security.getBasic()
|
|
||||||
.getPath());
|
String[] paths = getSecurePaths();
|
||||||
matcher.authenticationEntryPoint(entryPoint()).antMatcher("/**")
|
|
||||||
.httpBasic().authenticationEntryPoint(entryPoint()).and()
|
HttpConfiguration matcher = http.requestMatchers().antMatchers(paths);
|
||||||
.anonymous().disable();
|
matcher.authenticationEntryPoint(entryPoint()).httpBasic()
|
||||||
matcher.authorizeUrls().antMatchers("/**")
|
.authenticationEntryPoint(entryPoint()).and().anonymous()
|
||||||
|
.disable();
|
||||||
|
matcher.authorizeUrls().anyRequest()
|
||||||
.hasRole(this.security.getBasic().getRole());
|
.hasRole(this.security.getBasic().getRole());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No cookies for service endpoints by default
|
// No cookies for service endpoints by default
|
||||||
http.sessionManagement().sessionCreationPolicy(this.security.getSessions());
|
http.sessionManagement().sessionCreationPolicy(this.security.getSessions());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] getSecurePaths() {
|
||||||
|
List<String> list = new ArrayList<String>();
|
||||||
|
for (String path : this.security.getBasic().getPath()) {
|
||||||
|
path = path == null ? "" : path.trim();
|
||||||
|
if (path.equals("/**")) {
|
||||||
|
return new String[] { path };
|
||||||
|
}
|
||||||
|
if (!path.equals("")) {
|
||||||
|
list.add(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list.addAll(Arrays.asList(this.endpoints.getSecurePaths()));
|
||||||
|
return list.toArray(new String[list.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AuthenticationEntryPoint entryPoint() {
|
private AuthenticationEntryPoint entryPoint() {
|
||||||
@ -100,9 +133,8 @@ public class SecurityAutoConfiguration {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configure(WebSecurityBuilder builder) throws Exception {
|
public void configure(WebSecurityBuilder builder) throws Exception {
|
||||||
builder.ignoring().antMatchers(this.endpoints.getHealth().getPath(),
|
builder.ignoring().antMatchers(this.security.getIgnored())
|
||||||
this.endpoints.getInfo().getPath(),
|
.antMatchers(this.endpoints.getOpenPaths());
|
||||||
this.endpoints.getError().getPath());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -117,7 +149,7 @@ public class SecurityAutoConfiguration {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ConditionalOnMissingBean(AuthenticationManager.class)
|
@Conditional(NoUserSuppliedAuthenticationManager.class)
|
||||||
@Configuration
|
@Configuration
|
||||||
public static class AuthenticationManagerConfiguration {
|
public static class AuthenticationManagerConfiguration {
|
||||||
|
|
||||||
@ -130,4 +162,21 @@ public class SecurityAutoConfiguration {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class NoUserSuppliedAuthenticationManager implements Condition {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||||
|
String[] beans = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
|
||||||
|
context.getBeanFactory(), AuthenticationManager.class, false, false);
|
||||||
|
for (String bean : beans) {
|
||||||
|
if (!BeanIds.AUTHENTICATION_MANAGER.equals(bean)) {
|
||||||
|
// Not the one supplied by Spring Security automatically
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,8 @@ public class SecurityProperties {
|
|||||||
|
|
||||||
private SessionCreationPolicy sessions = SessionCreationPolicy.stateless;
|
private SessionCreationPolicy sessions = SessionCreationPolicy.stateless;
|
||||||
|
|
||||||
private String[] ignored = new String[0];
|
private String[] ignored = new String[] { "/css/**", "/js/**", "/images/**",
|
||||||
|
"/**/favicon.ico" };
|
||||||
|
|
||||||
public SessionCreationPolicy getSessions() {
|
public SessionCreationPolicy getSessions() {
|
||||||
return this.sessions;
|
return this.sessions;
|
||||||
@ -73,7 +74,7 @@ public class SecurityProperties {
|
|||||||
|
|
||||||
private String realm = "Spring";
|
private String realm = "Spring";
|
||||||
|
|
||||||
private String path = "/**";
|
private String[] path = new String[] { "/**" };
|
||||||
|
|
||||||
private String role = "USER";
|
private String role = "USER";
|
||||||
|
|
||||||
@ -93,12 +94,12 @@ public class SecurityProperties {
|
|||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPath() {
|
public String[] getPath() {
|
||||||
return this.path;
|
return this.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPath(String path) {
|
public void setPath(String... paths) {
|
||||||
this.path = path;
|
this.path = paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRole() {
|
public String getRole() {
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.bootstrap.actuate.autoconfigure;
|
package org.springframework.bootstrap.actuate.autoconfigure;
|
||||||
|
|
||||||
|
import javax.servlet.Filter;
|
||||||
import javax.servlet.Servlet;
|
import javax.servlet.Servlet;
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
@ -133,6 +134,14 @@ public class ManagementConfigurationTests {
|
|||||||
public Dynamic addServlet(String servletName, Servlet servlet) {
|
public Dynamic addServlet(String servletName, Servlet servlet) {
|
||||||
return Mockito.mock(Dynamic.class);
|
return Mockito.mock(Dynamic.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public javax.servlet.FilterRegistration.Dynamic addFilter(
|
||||||
|
String filterName, Filter filter) {
|
||||||
|
// TODO: remove this when @ConditionalOnBean works
|
||||||
|
return Mockito
|
||||||
|
.mock(javax.servlet.FilterRegistration.Dynamic.class);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
for (ServletContextInitializer initializer : initializers) {
|
for (ServletContextInitializer initializer : initializers) {
|
||||||
try {
|
try {
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2013 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.bootstrap.actuate.properties;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.MutablePropertyValues;
|
||||||
|
import org.springframework.bootstrap.bind.RelaxedDataBinder;
|
||||||
|
import org.springframework.core.convert.support.DefaultConversionService;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class SecurityPropertiesTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBindingIgnoredSingleValued() {
|
||||||
|
SecurityProperties security = new SecurityProperties();
|
||||||
|
RelaxedDataBinder binder = new RelaxedDataBinder(security, "security");
|
||||||
|
binder.bind(new MutablePropertyValues(Collections.singletonMap(
|
||||||
|
"security.ignored", "/css/**")));
|
||||||
|
assertFalse(binder.getBindingResult().hasErrors());
|
||||||
|
assertEquals(1, security.getIgnored().length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBindingIgnoredMultiValued() {
|
||||||
|
SecurityProperties security = new SecurityProperties();
|
||||||
|
RelaxedDataBinder binder = new RelaxedDataBinder(security, "security");
|
||||||
|
binder.setConversionService(new DefaultConversionService());
|
||||||
|
binder.bind(new MutablePropertyValues(Collections.singletonMap(
|
||||||
|
"security.ignored", "/css/**,/images/**")));
|
||||||
|
assertFalse(binder.getBindingResult().hasErrors());
|
||||||
|
assertEquals(2, security.getIgnored().length);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -74,6 +74,7 @@ public class SpringBootstrapCompilerAutoConfiguration extends CompilerAutoConfig
|
|||||||
"org.springframework.context.annotation.Bean",
|
"org.springframework.context.annotation.Bean",
|
||||||
"org.springframework.context.ApplicationContext",
|
"org.springframework.context.ApplicationContext",
|
||||||
"org.springframework.context.MessageSource",
|
"org.springframework.context.MessageSource",
|
||||||
|
"org.springframework.core.annotation.Order",
|
||||||
"org.springframework.core.io.ResourceLoader",
|
"org.springframework.core.io.ResourceLoader",
|
||||||
"org.springframework.bootstrap.CommandLineRunner",
|
"org.springframework.bootstrap.CommandLineRunner",
|
||||||
"org.springframework.bootstrap.context.annotation.EnableAutoConfiguration");
|
"org.springframework.bootstrap.context.annotation.EnableAutoConfiguration");
|
||||||
|
@ -28,7 +28,6 @@ import org.springframework.web.client.DefaultResponseErrorHandler;
|
|||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integration tests for separate management and main service ports.
|
* Integration tests for separate management and main service ports.
|
||||||
@ -83,10 +82,7 @@ public class ManagementAddressServiceBootstrapApplicationTests {
|
|||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
ResponseEntity<Map> entity = getRestTemplate().getForEntity(
|
ResponseEntity<Map> entity = getRestTemplate().getForEntity(
|
||||||
"http://localhost:" + managementPort + "/metrics", Map.class);
|
"http://localhost:" + managementPort + "/metrics", Map.class);
|
||||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
assertEquals(HttpStatus.UNAUTHORIZED, entity.getStatusCode());
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Map<String, Object> body = entity.getBody();
|
|
||||||
assertTrue("Wrong body: " + body, body.containsKey("counter.status.200.root"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -4,19 +4,14 @@ import java.util.Date;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.bootstrap.SpringApplication;
|
import org.springframework.bootstrap.SpringApplication;
|
||||||
import org.springframework.bootstrap.actuate.autoconfigure.ConditionalOnManagementContext;
|
import org.springframework.bootstrap.actuate.properties.SecurityProperties;
|
||||||
import org.springframework.bootstrap.actuate.autoconfigure.ManagementAutoConfiguration;
|
|
||||||
import org.springframework.bootstrap.actuate.autoconfigure.SecurityAutoConfiguration;
|
|
||||||
import org.springframework.bootstrap.context.annotation.ConditionalOnExpression;
|
|
||||||
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
|
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.context.annotation.Import;
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class,
|
@EnableAutoConfiguration
|
||||||
ManagementAutoConfiguration.class })
|
|
||||||
@ComponentScan
|
@ComponentScan
|
||||||
@Controller
|
@Controller
|
||||||
public class ActuatorUiBootstrapApplication {
|
public class ActuatorUiBootstrapApplication {
|
||||||
@ -33,19 +28,11 @@ public class ActuatorUiBootstrapApplication {
|
|||||||
SpringApplication.run(ActuatorUiBootstrapApplication.class, args);
|
SpringApplication.run(ActuatorUiBootstrapApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Bean
|
||||||
@ConditionalOnExpression("${server.port:8080} != ${management.port:${server.port:8080}}")
|
public SecurityProperties securityProperties() {
|
||||||
@Import(ManagementAutoConfiguration.class)
|
SecurityProperties security = new SecurityProperties();
|
||||||
protected static class ManagementConfiguration {
|
security.getBasic().setPath(""); // empty
|
||||||
|
return security;
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@ConditionalOnExpression("${server.port:8080} != ${management.port:${server.port:8080}}")
|
|
||||||
@ConditionalOnManagementContext
|
|
||||||
@Import(SecurityAutoConfiguration.class)
|
|
||||||
protected static class ManagementSecurityConfiguration {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.springframework.bootstrap.sample.ui;
|
package org.springframework.bootstrap.sample.ui;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
@ -68,6 +69,14 @@ public class ActuatorUiBootstrapApplicationTests {
|
|||||||
assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody().contains("body"));
|
assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody().contains("body"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMetrics() throws Exception {
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
ResponseEntity<Map> entity = getRestTemplate().getForEntity(
|
||||||
|
"http://localhost:8080/metrics", Map.class);
|
||||||
|
assertEquals(HttpStatus.UNAUTHORIZED, entity.getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
private RestTemplate getRestTemplate() {
|
private RestTemplate getRestTemplate() {
|
||||||
RestTemplate restTemplate = new RestTemplate();
|
RestTemplate restTemplate = new RestTemplate();
|
||||||
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
|
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
|
||||||
|
@ -23,6 +23,7 @@ import org.springframework.bootstrap.bind.PropertiesConfigurationFactory;
|
|||||||
import org.springframework.bootstrap.context.annotation.EnableConfigurationPropertiesImportSelector.ConfigurationPropertiesHolder;
|
import org.springframework.bootstrap.context.annotation.EnableConfigurationPropertiesImportSelector.ConfigurationPropertiesHolder;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.core.convert.ConversionService;
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.support.DefaultConversionService;
|
||||||
import org.springframework.core.env.PropertySources;
|
import org.springframework.core.env.PropertySources;
|
||||||
import org.springframework.validation.Validator;
|
import org.springframework.validation.Validator;
|
||||||
|
|
||||||
@ -40,6 +41,8 @@ public class PropertySourcesBindingPostProcessor implements BeanPostProcessor {
|
|||||||
|
|
||||||
private ConversionService conversionService;
|
private ConversionService conversionService;
|
||||||
|
|
||||||
|
private DefaultConversionService defaultConversionService = new DefaultConversionService();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param propertySources
|
* @param propertySources
|
||||||
*/
|
*/
|
||||||
@ -81,7 +84,10 @@ public class PropertySourcesBindingPostProcessor implements BeanPostProcessor {
|
|||||||
target);
|
target);
|
||||||
factory.setPropertySources(this.propertySources);
|
factory.setPropertySources(this.propertySources);
|
||||||
factory.setValidator(this.validator);
|
factory.setValidator(this.validator);
|
||||||
factory.setConversionService(this.conversionService);
|
// If no explicit conversion service is provided we add one so that (at least)
|
||||||
|
// comma-separated arrays of convertibles can be bound automatically
|
||||||
|
factory.setConversionService(this.conversionService == null ? this.defaultConversionService
|
||||||
|
: this.conversionService);
|
||||||
String targetName = null;
|
String targetName = null;
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
|
factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
|
||||||
|
@ -48,6 +48,15 @@ public class EnableConfigurationPropertiesTests {
|
|||||||
assertEquals("foo", this.context.getBean(TestProperties.class).getName());
|
assertEquals("foo", this.context.getBean(TestProperties.class).getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testArrayPropertiesBinding() {
|
||||||
|
this.context.register(TestConfiguration.class);
|
||||||
|
TestUtils.addEnviroment(this.context, "name:foo", "array:1,2,3");
|
||||||
|
this.context.refresh();
|
||||||
|
assertEquals(1, this.context.getBeanNamesForType(TestProperties.class).length);
|
||||||
|
assertEquals(3, this.context.getBean(TestProperties.class).getArray().length);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPropertiesBindingWithoutAnnotation() {
|
public void testPropertiesBindingWithoutAnnotation() {
|
||||||
this.context.register(MoreConfiguration.class);
|
this.context.register(MoreConfiguration.class);
|
||||||
@ -186,6 +195,7 @@ public class EnableConfigurationPropertiesTests {
|
|||||||
@ConfigurationProperties
|
@ConfigurationProperties
|
||||||
protected static class TestProperties {
|
protected static class TestProperties {
|
||||||
private String name;
|
private String name;
|
||||||
|
private int[] array;
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return this.name;
|
return this.name;
|
||||||
@ -194,6 +204,14 @@ public class EnableConfigurationPropertiesTests {
|
|||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setArray(int... values) {
|
||||||
|
this.array = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getArray() {
|
||||||
|
return this.array;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class MoreProperties {
|
protected static class MoreProperties {
|
||||||
|
Loading…
Reference in New Issue
Block a user