diff --git a/config/src/main/kotlin/org/springframework/security/config/web/servlet/HttpSecurityDsl.kt b/config/src/main/kotlin/org/springframework/security/config/web/servlet/HttpSecurityDsl.kt index 8e0b790177..170d80a797 100644 --- a/config/src/main/kotlin/org/springframework/security/config/web/servlet/HttpSecurityDsl.kt +++ b/config/src/main/kotlin/org/springframework/security/config/web/servlet/HttpSecurityDsl.kt @@ -18,9 +18,11 @@ package org.springframework.security.config.web.servlet import org.springframework.context.ApplicationContext import org.springframework.security.authentication.AuthenticationManager +import org.springframework.security.config.annotation.SecurityConfigurerAdapter import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository +import org.springframework.security.web.DefaultSecurityFilterChain import org.springframework.security.web.util.matcher.RequestMatcher import org.springframework.util.ClassUtils import javax.servlet.Filter @@ -75,6 +77,35 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu var authenticationManager: AuthenticationManager? = null + /** + * Applies a [SecurityConfigurerAdapter] to this [HttpSecurity] + * + * Example: + * + * ``` + * @Configuration + * @EnableWebSecurity + * class SecurityConfig { + * + * @Bean + * fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { + * http { + * apply(CustomSecurityConfigurer()) { + * customProperty = "..." + * } + * } + * return http.build() + * } + * } + * ``` + * + * @param configurer + * the [SecurityConfigurerAdapter] for further customizations + */ + fun > apply(configurer: C, configuration: C.() -> Unit = { }): C { + return this.http.apply(configurer).apply(configuration) + } + /** * Allows configuring the [HttpSecurity] to only be invoked when matching the * provided pattern. diff --git a/config/src/test/kotlin/org/springframework/security/config/web/servlet/HttpSecurityDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/web/servlet/HttpSecurityDslTests.kt index 1e981ac57f..1b147429b2 100644 --- a/config/src/test/kotlin/org/springframework/security/config/web/servlet/HttpSecurityDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/web/servlet/HttpSecurityDslTests.kt @@ -22,6 +22,8 @@ import io.mockk.verify import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @@ -30,9 +32,11 @@ import org.springframework.security.authentication.AuthenticationManager import org.springframework.security.authentication.ProviderManager import org.springframework.security.authentication.TestingAuthenticationProvider import org.springframework.security.authentication.TestingAuthenticationToken +import org.springframework.security.config.annotation.web.HttpSecurityBuilder import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer import org.springframework.security.config.test.SpringTestContext import org.springframework.security.config.test.SpringTestContextExtension import org.springframework.security.core.userdetails.User @@ -40,6 +44,7 @@ import org.springframework.security.core.userdetails.UserDetailsService import org.springframework.security.provisioning.InMemoryUserDetailsManager import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic import org.springframework.security.web.FilterChainProxy +import org.springframework.security.web.SecurityFilterChain import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter import org.springframework.security.web.server.header.ContentTypeOptionsServerHttpHeadersWriter @@ -53,9 +58,6 @@ import org.springframework.test.web.servlet.post import org.springframework.test.web.servlet.request.MockMvcRequestBuilders import org.springframework.web.servlet.config.annotation.EnableWebMvc import javax.servlet.Filter -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.ValueSource -import org.springframework.security.web.SecurityFilterChain /** * Tests for [HttpSecurityDsl] @@ -483,4 +485,42 @@ class HttpSecurityDslTests { } class CustomFilter : UsernamePasswordAuthenticationFilter() + + @Test + fun `HTTP security when apply custom security configurer then custom filter added to filter chain`() { + this.spring.register(CustomSecurityConfigurerConfig::class.java).autowire() + + val filterChain = spring.context.getBean(FilterChainProxy::class.java) + val filterClasses: List> = filterChain.getFilters("/").map { it.javaClass } + + assertThat(filterClasses).contains( + CustomFilter::class.java + ) + } + + @Configuration + @EnableWebSecurity + @EnableWebMvc + open class CustomSecurityConfigurerConfig { + @Bean + open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { + http { + apply(CustomSecurityConfigurer()) { + filter = CustomFilter() + } + } + return http.build() + } + } + + class CustomSecurityConfigurer> : AbstractHttpConfigurer, H>() { + var filter: Filter? = null + override fun init(builder: H) { + filter = filter ?: UsernamePasswordAuthenticationFilter() + } + + override fun configure(builder: H) { + builder.addFilterBefore(CustomFilter(), UsernamePasswordAuthenticationFilter::class.java) + } + } }