mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-25 19:09:03 +00:00
Merge branch '4.0.x'
Closes gh-50190
This commit is contained in:
+5
-1
@@ -38,6 +38,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.WebFilterChainProxy;
|
||||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.cors.reactive.PreFlightRequestHandler;
|
||||
import org.springframework.web.cors.reactive.PreFlightRequestWebFilter;
|
||||
|
||||
@@ -63,7 +64,10 @@ public final class ReactiveManagementWebSecurityAutoConfiguration {
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, PreFlightRequestHandler handler) {
|
||||
http.authorizeExchange((exchanges) -> {
|
||||
exchanges.matchers(healthMatcher(), additionalHealthPathsMatcher()).permitAll();
|
||||
if (ClassUtils.isPresent("org.springframework.boot.health.actuate.endpoint.HealthEndpoint",
|
||||
getClass().getClassLoader())) {
|
||||
exchanges.matchers(healthMatcher(), additionalHealthPathsMatcher()).permitAll();
|
||||
}
|
||||
exchanges.anyExchange().authenticated();
|
||||
});
|
||||
PreFlightRequestWebFilter filter = new PreFlightRequestWebFilter(handler);
|
||||
|
||||
+6
-6
@@ -59,13 +59,13 @@ public final class ManagementWebSecurityAutoConfiguration {
|
||||
@Bean
|
||||
@Order(SecurityFilterProperties.BASIC_AUTH_ORDER)
|
||||
SecurityFilterChain managementSecurityFilterChain(Environment environment, HttpSecurity http) {
|
||||
if (ClassUtils.isPresent("org.springframework.boot.health.actuate.endpoint.HealthEndpoint",
|
||||
getClass().getClassLoader())) {
|
||||
http.authorizeHttpRequests((requests) -> {
|
||||
http.authorizeHttpRequests((requests) -> {
|
||||
if (ClassUtils.isPresent("org.springframework.boot.health.actuate.endpoint.HealthEndpoint",
|
||||
getClass().getClassLoader())) {
|
||||
requests.requestMatchers(healthMatcher(), additionalHealthPathsMatcher()).permitAll();
|
||||
requests.anyRequest().authenticated();
|
||||
});
|
||||
}
|
||||
}
|
||||
requests.anyRequest().authenticated();
|
||||
});
|
||||
if (ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", null)) {
|
||||
http.cors(withDefaults());
|
||||
}
|
||||
|
||||
+55
-20
@@ -36,6 +36,7 @@ import org.springframework.boot.health.autoconfigure.registry.HealthContributorR
|
||||
import org.springframework.boot.security.autoconfigure.web.reactive.ReactiveWebSecurityAutoConfiguration;
|
||||
import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
|
||||
import org.springframework.boot.webflux.autoconfigure.WebFluxAutoConfiguration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
@@ -68,22 +69,26 @@ import static org.springframework.security.config.Customizer.withDefaults;
|
||||
class ReactiveManagementWebSecurityAutoConfigurationTests {
|
||||
|
||||
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
|
||||
InfoEndpointAutoConfiguration.class, WebFluxAutoConfiguration.class,
|
||||
.withConfiguration(AutoConfigurations.of(InfoEndpointAutoConfiguration.class, WebFluxAutoConfiguration.class,
|
||||
EnvironmentEndpointAutoConfiguration.class, EndpointAutoConfiguration.class,
|
||||
WebEndpointAutoConfiguration.class, ReactiveWebSecurityAutoConfiguration.class,
|
||||
ReactiveManagementWebSecurityAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void permitAllForHealth() {
|
||||
this.contextRunner.withUserConfiguration(UserDetailsServiceConfiguration.class)
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.withUserConfiguration(UserDetailsServiceConfiguration.class)
|
||||
.run((context) -> assertThat(getAuthenticateHeader(context, "/actuator/health")).isNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
void withAdditionalPathsOnSamePort() {
|
||||
this.contextRunner.withUserConfiguration(UserDetailsServiceConfiguration.class)
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.withUserConfiguration(UserDetailsServiceConfiguration.class)
|
||||
.withPropertyValues("management.endpoint.health.group.test1.include=*",
|
||||
"management.endpoint.health.group.test2.include=*",
|
||||
"management.endpoint.health.group.test1.additional-path=server:/check1",
|
||||
@@ -97,7 +102,10 @@ class ReactiveManagementWebSecurityAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void withAdditionalPathsOnDifferentPort() {
|
||||
this.contextRunner.withUserConfiguration(UserDetailsServiceConfiguration.class)
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.withUserConfiguration(UserDetailsServiceConfiguration.class)
|
||||
.withPropertyValues("management.endpoint.health.group.test1.include=*",
|
||||
"management.endpoint.health.group.test2.include=*",
|
||||
"management.endpoint.health.group.test1.additional-path=server:/check1",
|
||||
@@ -112,6 +120,19 @@ class ReactiveManagementWebSecurityAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void securesEverythingElse() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.withUserConfiguration(UserDetailsServiceConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(getRequiredAuthenticateHeader(context, "/actuator").get(0)).contains("Basic realm=");
|
||||
assertThat(getRequiredAuthenticateHeader(context, "/foo").toString()).contains("Basic realm=");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions(packages = "org.springframework.boot.health.actuate.endpoint")
|
||||
void securesEverythingElseWhenHealthIsAbsent() {
|
||||
this.contextRunner.withUserConfiguration(UserDetailsServiceConfiguration.class).run((context) -> {
|
||||
assertThat(getRequiredAuthenticateHeader(context, "/actuator").get(0)).contains("Basic realm=");
|
||||
assertThat(getRequiredAuthenticateHeader(context, "/foo").toString()).contains("Basic realm=");
|
||||
@@ -120,16 +141,22 @@ class ReactiveManagementWebSecurityAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void noExistingAuthenticationManagerOrUserDetailsService() {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(getAuthenticateHeader(context, "/actuator/health")).isNull();
|
||||
assertThat(getRequiredAuthenticateHeader(context, "/actuator").get(0)).contains("Basic realm=");
|
||||
assertThat(getRequiredAuthenticateHeader(context, "/foo").toString()).contains("Basic realm=");
|
||||
});
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.run((context) -> {
|
||||
assertThat(getAuthenticateHeader(context, "/actuator/health")).isNull();
|
||||
assertThat(getRequiredAuthenticateHeader(context, "/actuator").get(0)).contains("Basic realm=");
|
||||
assertThat(getRequiredAuthenticateHeader(context, "/foo").toString()).contains("Basic realm=");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesMatchersBasedOffConfiguredActuatorBasePath() {
|
||||
this.contextRunner.withUserConfiguration(UserDetailsServiceConfiguration.class)
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.withUserConfiguration(UserDetailsServiceConfiguration.class)
|
||||
.withPropertyValues("management.endpoints.web.base-path=/")
|
||||
.run((context) -> {
|
||||
assertThat(getAuthenticateHeader(context, "/health")).isNull();
|
||||
@@ -139,18 +166,26 @@ class ReactiveManagementWebSecurityAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void backsOffIfCustomSecurityIsAdded() {
|
||||
this.contextRunner.withUserConfiguration(CustomSecurityConfiguration.class).run((context) -> {
|
||||
assertThat(getRequiredLocationHeader(context, "/actuator/health").toString()).contains("/login");
|
||||
assertThat(getLocationHeader(context, "/foo")).isNull();
|
||||
});
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.withUserConfiguration(CustomSecurityConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(getRequiredLocationHeader(context, "/actuator/health").toString()).contains("/login");
|
||||
assertThat(getLocationHeader(context, "/foo")).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void backsOffWhenWebFilterChainProxyBeanPresent() {
|
||||
this.contextRunner.withUserConfiguration(WebFilterChainProxyConfiguration.class).run((context) -> {
|
||||
assertThat(getRequiredLocationHeader(context, "/actuator/health").toString()).contains("/login");
|
||||
assertThat(getRequiredLocationHeader(context, "/foo").toString()).contains("/login");
|
||||
});
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.withUserConfiguration(WebFilterChainProxyConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(getRequiredLocationHeader(context, "/actuator/health").toString()).contains("/login");
|
||||
assertThat(getRequiredLocationHeader(context, "/foo").toString()).contains("/login");
|
||||
});
|
||||
}
|
||||
|
||||
private @Nullable List<String> getAuthenticateHeader(AssertableReactiveWebApplicationContext context, String path) {
|
||||
|
||||
+55
-25
@@ -37,6 +37,7 @@ import org.springframework.boot.security.autoconfigure.web.servlet.ServletWebSec
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
|
||||
import org.springframework.boot.web.context.servlet.AnnotationConfigServletWebApplicationContext;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.boot.web.server.context.WebServerApplicationContext;
|
||||
@@ -71,12 +72,10 @@ class ManagementWebSecurityAutoConfigurationTests {
|
||||
|
||||
private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner(contextSupplier(),
|
||||
WebServerApplicationContext.class)
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class,
|
||||
InfoEndpointAutoConfiguration.class, EnvironmentEndpointAutoConfiguration.class,
|
||||
EndpointAutoConfiguration.class, WebMvcAutoConfiguration.class, WebEndpointAutoConfiguration.class,
|
||||
SecurityAutoConfiguration.class, ServletWebSecurityAutoConfiguration.class,
|
||||
ManagementWebSecurityAutoConfiguration.class));
|
||||
.withConfiguration(AutoConfigurations.of(InfoEndpointAutoConfiguration.class,
|
||||
EnvironmentEndpointAutoConfiguration.class, EndpointAutoConfiguration.class,
|
||||
WebMvcAutoConfiguration.class, WebEndpointAutoConfiguration.class, SecurityAutoConfiguration.class,
|
||||
ServletWebSecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class));
|
||||
|
||||
private static Supplier<ConfigurableWebApplicationContext> contextSupplier() {
|
||||
return WebApplicationContextRunner.withMockServletContext(MockWebServerApplicationContext::new);
|
||||
@@ -84,15 +83,32 @@ class ManagementWebSecurityAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void permitAllForHealth() {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).hasBean(MANAGEMENT_SECURITY_FILTER_CHAIN_BEAN);
|
||||
HttpStatus status = getResponseStatus(context, "/actuator/health");
|
||||
assertThat(status).isEqualTo(HttpStatus.OK);
|
||||
});
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.run((context) -> {
|
||||
assertThat(context).hasBean(MANAGEMENT_SECURITY_FILTER_CHAIN_BEAN);
|
||||
HttpStatus status = getResponseStatus(context, "/actuator/health");
|
||||
assertThat(status).isEqualTo(HttpStatus.OK);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void securesEverythingElse() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.run((context) -> {
|
||||
HttpStatus status = getResponseStatus(context, "/actuator");
|
||||
assertThat(status).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
status = getResponseStatus(context, "/foo");
|
||||
assertThat(status).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@ClassPathExclusions(packages = "org.springframework.boot.health.actuate.endpoint")
|
||||
void securesEverythingElseWhenHealthIsAbsent() {
|
||||
this.contextRunner.run((context) -> {
|
||||
HttpStatus status = getResponseStatus(context, "/actuator");
|
||||
assertThat(status).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
@@ -112,20 +128,28 @@ class ManagementWebSecurityAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void usesMatchersBasedOffConfiguredActuatorBasePath() {
|
||||
this.contextRunner.withPropertyValues("management.endpoints.web.base-path=/").run((context) -> {
|
||||
HttpStatus status = getResponseStatus(context, "/health");
|
||||
assertThat(status).isEqualTo(HttpStatus.OK);
|
||||
});
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.withPropertyValues("management.endpoints.web.base-path=/")
|
||||
.run((context) -> {
|
||||
HttpStatus status = getResponseStatus(context, "/health");
|
||||
assertThat(status).isEqualTo(HttpStatus.OK);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void backOffIfCustomSecurityIsAdded() {
|
||||
this.contextRunner.withUserConfiguration(CustomSecurityConfiguration.class).run((context) -> {
|
||||
HttpStatus status = getResponseStatus(context, "/actuator/health");
|
||||
assertThat(status).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
status = getResponseStatus(context, "/foo");
|
||||
assertThat(status).isEqualTo(HttpStatus.OK);
|
||||
});
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.withUserConfiguration(CustomSecurityConfiguration.class)
|
||||
.run((context) -> {
|
||||
HttpStatus status = getResponseStatus(context, "/actuator/health");
|
||||
assertThat(status).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
status = getResponseStatus(context, "/foo");
|
||||
assertThat(status).isEqualTo(HttpStatus.OK);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -155,6 +179,8 @@ class ManagementWebSecurityAutoConfigurationTests {
|
||||
@Test
|
||||
void withAdditionalPathsOnSamePort() {
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.withPropertyValues("management.endpoint.health.group.test1.include=*",
|
||||
"management.endpoint.health.group.test2.include=*",
|
||||
"management.endpoint.health.group.test1.additional-path=server:/check1",
|
||||
@@ -168,10 +194,14 @@ class ManagementWebSecurityAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void withAdditionalPathsOnDifferentPort() {
|
||||
this.contextRunner.withPropertyValues("management.endpoint.health.group.test1.include=*",
|
||||
"management.endpoint.health.group.test2.include=*",
|
||||
"management.endpoint.health.group.test1.additional-path=server:/check1",
|
||||
"management.endpoint.health.group.test2.additional-path=management:/check2", "management.server.port=0")
|
||||
this.contextRunner
|
||||
.withConfiguration(AutoConfigurations.of(HealthContributorAutoConfiguration.class,
|
||||
HealthContributorRegistryAutoConfiguration.class, HealthEndpointAutoConfiguration.class))
|
||||
.withPropertyValues("management.endpoint.health.group.test1.include=*",
|
||||
"management.endpoint.health.group.test2.include=*",
|
||||
"management.endpoint.health.group.test1.additional-path=server:/check1",
|
||||
"management.endpoint.health.group.test2.additional-path=management:/check2",
|
||||
"management.server.port=0")
|
||||
.run((context) -> {
|
||||
assertThat(getResponseStatus(context, "/check1")).isEqualTo(HttpStatus.OK);
|
||||
assertThat(getResponseStatus(context, "/check2")).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
|
||||
Reference in New Issue
Block a user