mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-07-05 00:56:58 +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
|
||||
|
||||
## 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
|
||||
|
||||
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
|
||||
you can test it like this:
|
||||
|
||||
$ mvn user:password@localhost:8080/info
|
||||
$ mvn user:password@localhost:8080/metrics
|
||||
... 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
|
||||
|
||||
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.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.)
|
||||
|
||||
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
|
||||
|
||||
|
@ -16,12 +16,22 @@
|
||||
|
||||
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.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.bootstrap.actuate.endpoint.error.ErrorEndpoint;
|
||||
import org.springframework.bootstrap.actuate.properties.ManagementServerProperties;
|
||||
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.EmbeddedServletContainerCustomizer;
|
||||
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.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
@ -43,6 +55,7 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
@Import(ManagementSecurityConfiguration.class)
|
||||
public class ManagementServerConfiguration {
|
||||
|
||||
@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;
|
||||
|
||||
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.bootstrap.actuate.properties.EndpointsProperties;
|
||||
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.EnableConfigurationProperties;
|
||||
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.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
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.web.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.HttpConfiguration;
|
||||
@ -58,6 +68,7 @@ public class SecurityAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean({ BoostrapWebSecurityConfigurerAdapter.class })
|
||||
public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter() {
|
||||
return new BoostrapWebSecurityConfigurerAdapter();
|
||||
}
|
||||
@ -76,20 +87,42 @@ public class SecurityAutoConfiguration {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpConfiguration http) throws Exception {
|
||||
|
||||
if (this.security.isRequireSsl()) {
|
||||
http.requiresChannel().antMatchers("/**").requiresSecure();
|
||||
http.requiresChannel().anyRequest().requiresSecure();
|
||||
}
|
||||
|
||||
if (this.security.getBasic().isEnabled()) {
|
||||
HttpConfiguration matcher = http.antMatcher(this.security.getBasic()
|
||||
.getPath());
|
||||
matcher.authenticationEntryPoint(entryPoint()).antMatcher("/**")
|
||||
.httpBasic().authenticationEntryPoint(entryPoint()).and()
|
||||
.anonymous().disable();
|
||||
matcher.authorizeUrls().antMatchers("/**")
|
||||
|
||||
String[] paths = getSecurePaths();
|
||||
|
||||
HttpConfiguration matcher = http.requestMatchers().antMatchers(paths);
|
||||
matcher.authenticationEntryPoint(entryPoint()).httpBasic()
|
||||
.authenticationEntryPoint(entryPoint()).and().anonymous()
|
||||
.disable();
|
||||
matcher.authorizeUrls().anyRequest()
|
||||
.hasRole(this.security.getBasic().getRole());
|
||||
|
||||
}
|
||||
|
||||
// No cookies for service endpoints by default
|
||||
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() {
|
||||
@ -100,9 +133,8 @@ public class SecurityAutoConfiguration {
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurityBuilder builder) throws Exception {
|
||||
builder.ignoring().antMatchers(this.endpoints.getHealth().getPath(),
|
||||
this.endpoints.getInfo().getPath(),
|
||||
this.endpoints.getError().getPath());
|
||||
builder.ignoring().antMatchers(this.security.getIgnored())
|
||||
.antMatchers(this.endpoints.getOpenPaths());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -117,7 +149,7 @@ public class SecurityAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnMissingBean(AuthenticationManager.class)
|
||||
@Conditional(NoUserSuppliedAuthenticationManager.class)
|
||||
@Configuration
|
||||
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 String[] ignored = new String[0];
|
||||
private String[] ignored = new String[] { "/css/**", "/js/**", "/images/**",
|
||||
"/**/favicon.ico" };
|
||||
|
||||
public SessionCreationPolicy getSessions() {
|
||||
return this.sessions;
|
||||
@ -73,7 +74,7 @@ public class SecurityProperties {
|
||||
|
||||
private String realm = "Spring";
|
||||
|
||||
private String path = "/**";
|
||||
private String[] path = new String[] { "/**" };
|
||||
|
||||
private String role = "USER";
|
||||
|
||||
@ -93,12 +94,12 @@ public class SecurityProperties {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
public String[] getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
public void setPath(String... paths) {
|
||||
this.path = paths;
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.bootstrap.actuate.autoconfigure;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
@ -133,6 +134,14 @@ public class ManagementConfigurationTests {
|
||||
public Dynamic addServlet(String servletName, Servlet servlet) {
|
||||
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) {
|
||||
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.ApplicationContext",
|
||||
"org.springframework.context.MessageSource",
|
||||
"org.springframework.core.annotation.Order",
|
||||
"org.springframework.core.io.ResourceLoader",
|
||||
"org.springframework.bootstrap.CommandLineRunner",
|
||||
"org.springframework.bootstrap.context.annotation.EnableAutoConfiguration");
|
||||
|
@ -28,7 +28,6 @@ import org.springframework.web.client.DefaultResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Integration tests for separate management and main service ports.
|
||||
@ -83,10 +82,7 @@ public class ManagementAddressServiceBootstrapApplicationTests {
|
||||
@SuppressWarnings("rawtypes")
|
||||
ResponseEntity<Map> entity = getRestTemplate().getForEntity(
|
||||
"http://localhost:" + managementPort + "/metrics", Map.class);
|
||||
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> body = entity.getBody();
|
||||
assertTrue("Wrong body: " + body, body.containsKey("counter.status.200.root"));
|
||||
assertEquals(HttpStatus.UNAUTHORIZED, entity.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -4,19 +4,14 @@ import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.bootstrap.SpringApplication;
|
||||
import org.springframework.bootstrap.actuate.autoconfigure.ConditionalOnManagementContext;
|
||||
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.actuate.properties.SecurityProperties;
|
||||
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
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.web.bind.annotation.RequestMapping;
|
||||
|
||||
@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class,
|
||||
ManagementAutoConfiguration.class })
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan
|
||||
@Controller
|
||||
public class ActuatorUiBootstrapApplication {
|
||||
@ -33,19 +28,11 @@ public class ActuatorUiBootstrapApplication {
|
||||
SpringApplication.run(ActuatorUiBootstrapApplication.class, args);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnExpression("${server.port:8080} != ${management.port:${server.port:8080}}")
|
||||
@Import(ManagementAutoConfiguration.class)
|
||||
protected static class ManagementConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnExpression("${server.port:8080} != ${management.port:${server.port:8080}}")
|
||||
@ConditionalOnManagementContext
|
||||
@Import(SecurityAutoConfiguration.class)
|
||||
protected static class ManagementSecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
public SecurityProperties securityProperties() {
|
||||
SecurityProperties security = new SecurityProperties();
|
||||
security.getBasic().setPath(""); // empty
|
||||
return security;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.springframework.bootstrap.sample.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
@ -68,6 +69,14 @@ public class ActuatorUiBootstrapApplicationTests {
|
||||
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() {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
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.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.env.PropertySources;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
@ -40,6 +41,8 @@ public class PropertySourcesBindingPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
private DefaultConversionService defaultConversionService = new DefaultConversionService();
|
||||
|
||||
/**
|
||||
* @param propertySources
|
||||
*/
|
||||
@ -81,7 +84,10 @@ public class PropertySourcesBindingPostProcessor implements BeanPostProcessor {
|
||||
target);
|
||||
factory.setPropertySources(this.propertySources);
|
||||
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;
|
||||
if (annotation != null) {
|
||||
factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
|
||||
|
@ -48,6 +48,15 @@ public class EnableConfigurationPropertiesTests {
|
||||
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
|
||||
public void testPropertiesBindingWithoutAnnotation() {
|
||||
this.context.register(MoreConfiguration.class);
|
||||
@ -186,6 +195,7 @@ public class EnableConfigurationPropertiesTests {
|
||||
@ConfigurationProperties
|
||||
protected static class TestProperties {
|
||||
private String name;
|
||||
private int[] array;
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
@ -194,6 +204,14 @@ public class EnableConfigurationPropertiesTests {
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setArray(int... values) {
|
||||
this.array = values;
|
||||
}
|
||||
|
||||
public int[] getArray() {
|
||||
return this.array;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class MoreProperties {
|
||||
|
Loading…
Reference in New Issue
Block a user