diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/ServletManagementChildContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/ServletManagementChildContextConfiguration.java index f5e8a310097b..159bcf49a9b9 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/ServletManagementChildContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/ServletManagementChildContextConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -50,6 +50,7 @@ import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean; import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -108,6 +109,13 @@ Filter springSecurityFilterChain(HierarchicalBeanFactory beanFactory) { return parent.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN, Filter.class); } + @Bean + @ConditionalOnBean(name = "securityFilterChainRegistration", search = SearchStrategy.ANCESTORS) + DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(HierarchicalBeanFactory beanFactory) { + return beanFactory.getParentBeanFactory().getBean("securityFilterChainRegistration", + DelegatingFilterProxyRegistrationBean.class); + } + } static class ServletManagementWebServerFactoryCustomizer diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementPortSampleActuatorApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementPortSampleActuatorApplicationTests.java index eddf52951155..f69f1001c9ca 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementPortSampleActuatorApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-actuator/src/test/java/smoketest/actuator/ManagementPortSampleActuatorApplicationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -19,14 +19,22 @@ import java.util.Map; import org.junit.jupiter.api.Test; +import smoketest.actuator.ManagementPortSampleActuatorApplicationTests.CustomErrorAttributes; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.web.server.LocalManagementPort; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.error.ErrorAttributeOptions; import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; +import org.springframework.context.annotation.Import; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.context.request.WebRequest; import static org.assertj.core.api.Assertions.assertThat; @@ -37,6 +45,7 @@ */ @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "management.server.port=0", "management.endpoint.health.show-details=always" }) +@Import(CustomErrorAttributes.class) class ManagementPortSampleActuatorApplicationTests { @LocalServerPort @@ -45,6 +54,9 @@ class ManagementPortSampleActuatorApplicationTests { @LocalManagementPort private int managementPort; + @Autowired + private CustomErrorAttributes errorAttributes; + @Test void testHome() { ResponseEntity> entity = asMapEntity( @@ -79,9 +91,31 @@ void testErrorPage() { assertThat(entity.getBody().get("status")).isEqualTo(999); } + @Test + void securityContextIsAvailableToErrorHandling() { + this.errorAttributes.securityContext = null; + ResponseEntity> entity = asMapEntity(new TestRestTemplate("user", "password") + .getForEntity("http://localhost:" + this.managementPort + "/404", Map.class)); + assertThat(this.errorAttributes.securityContext.getAuthentication()).isNotNull(); + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + assertThat(entity.getBody().get("status")).isEqualTo(404); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) static ResponseEntity> asMapEntity(ResponseEntity entity) { return (ResponseEntity) entity; } + static class CustomErrorAttributes extends DefaultErrorAttributes { + + private volatile SecurityContext securityContext; + + @Override + public Map getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) { + this.securityContext = SecurityContextHolder.getContext(); + return super.getErrorAttributes(webRequest, options); + } + + } + }