parent
23594b3d01
commit
a3e7e54b70
|
@ -50,6 +50,7 @@ import javax.servlet.http.HttpServletRequest
|
|||
* ```
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @author Norbert Nowak
|
||||
* @since 5.3
|
||||
* @param httpConfiguration the configurations to apply to [HttpSecurity]
|
||||
*/
|
||||
|
@ -905,4 +906,32 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
|
|||
init()
|
||||
authenticationManager?.also { this.http.authenticationManager(authenticationManager) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables security context configuration.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* @EnableWebSecurity
|
||||
* class SecurityConfig : WebSecurityConfigurerAdapter() {
|
||||
*
|
||||
* override fun configure(http: HttpSecurity) {
|
||||
* http {
|
||||
* securityContext {
|
||||
* securityContextRepository = SECURITY_CONTEXT_REPOSITORY
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @author Norbert Nowak
|
||||
* @since 5.7
|
||||
* @param securityContextConfiguration configuration to be applied to Security Context
|
||||
* @see [SecurityContextDsl]
|
||||
*/
|
||||
fun securityContext(securityContextConfiguration: SecurityContextDsl.() -> Unit) {
|
||||
val securityContextCustomizer = SecurityContextDsl().apply(securityContextConfiguration).get()
|
||||
this.http.securityContext(securityContextCustomizer)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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
|
||||
*
|
||||
* https://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.security.config.web.servlet
|
||||
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configurers.SecurityContextConfigurer
|
||||
import org.springframework.security.web.context.SecurityContextRepository
|
||||
|
||||
|
||||
/**
|
||||
* A Kotlin DSL to configure [HttpSecurity] security context using idiomatic Kotlin code.
|
||||
*
|
||||
* @property securityContextRepository the [SecurityContextRepository] used for persisting [org.springframework.security.core.context.SecurityContext] between requests
|
||||
* @author Norbert Nowak
|
||||
* @since 5.7
|
||||
*/
|
||||
@SecurityMarker
|
||||
class SecurityContextDsl {
|
||||
|
||||
var securityContextRepository: SecurityContextRepository? = null
|
||||
var requireExplicitSave: Boolean? = null
|
||||
|
||||
internal fun get(): (SecurityContextConfigurer<HttpSecurity>) -> Unit {
|
||||
return { securityContext ->
|
||||
securityContextRepository?.also { securityContext.securityContextRepository(it) }
|
||||
requireExplicitSave?.also { securityContext.requireExplicitSave(it) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright 2002-2022 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
|
||||
*
|
||||
* https://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.security.config.web.servlet
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertDoesNotThrow
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||
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.test.SpringTestContext
|
||||
import org.springframework.security.config.test.SpringTestContextExtension
|
||||
import org.springframework.security.core.context.SecurityContext
|
||||
import org.springframework.security.core.userdetails.PasswordEncodedUser
|
||||
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.context.*
|
||||
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
|
||||
|
||||
@ExtendWith(SpringTestContextExtension::class)
|
||||
class SecurityContextDslTests {
|
||||
|
||||
@JvmField
|
||||
val spring = SpringTestContext(this)
|
||||
|
||||
@Autowired
|
||||
lateinit var mvc: MockMvc
|
||||
|
||||
@Test
|
||||
fun `configure when registering object post processor then invoked on security context persistence filter`() {
|
||||
spring.register(ObjectPostProcessorConfig::class.java).autowire()
|
||||
verify { ObjectPostProcessorConfig.objectPostProcessor.postProcess(any<SecurityContextPersistenceFilter>()) }
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
open class ObjectPostProcessorConfig : WebSecurityConfigurerAdapter() {
|
||||
override fun configure(http: HttpSecurity) {
|
||||
// @formatter:off
|
||||
http {
|
||||
securityContext { }
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
open fun objectPostProcessor(): ObjectPostProcessor<Any> = objectPostProcessor
|
||||
|
||||
companion object {
|
||||
var objectPostProcessor: ObjectPostProcessor<Any> = spyk(ReflectingObjectPostProcessor())
|
||||
|
||||
class ReflectingObjectPostProcessor : ObjectPostProcessor<Any> {
|
||||
override fun <O> postProcess(`object`: O): O = `object`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `security context when invoked twice then uses original security context repository`() {
|
||||
spring.register(DuplicateDoesNotOverrideConfig::class.java).autowire()
|
||||
every { DuplicateDoesNotOverrideConfig.SECURITY_CONTEXT_REPOSITORY.loadContext(any<HttpRequestResponseHolder>()) } returns mockk<SecurityContext>(relaxed = true)
|
||||
mvc.perform(get("/"))
|
||||
verify(exactly = 1) { DuplicateDoesNotOverrideConfig.SECURITY_CONTEXT_REPOSITORY.loadContext(any<HttpRequestResponseHolder>()) }
|
||||
}
|
||||
|
||||
|
||||
@EnableWebSecurity
|
||||
open class DuplicateDoesNotOverrideConfig : WebSecurityConfigurerAdapter() {
|
||||
override fun configure(http: HttpSecurity) {
|
||||
// @formatter:off
|
||||
http {
|
||||
securityContext {
|
||||
securityContextRepository = SECURITY_CONTEXT_REPOSITORY
|
||||
}
|
||||
securityContext { }
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
companion object {
|
||||
var SECURITY_CONTEXT_REPOSITORY = mockk<SecurityContextRepository>(relaxed = true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `security context when security context repository not configured then does not throw exception`() {
|
||||
spring.register(SecurityContextRepositoryDefaultsSecurityContextRepositoryConfig::class.java).autowire()
|
||||
assertDoesNotThrow { mvc.perform(get("/")) }
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
open class SecurityContextRepositoryDefaultsSecurityContextRepositoryConfig : WebSecurityConfigurerAdapter(true) {
|
||||
override fun configure(http: HttpSecurity) {
|
||||
// @formatter:off
|
||||
http {
|
||||
addFilterAt<WebAsyncManagerIntegrationFilter>(WebAsyncManagerIntegrationFilter())
|
||||
anonymous { }
|
||||
securityContext { }
|
||||
authorizeRequests {
|
||||
authorize(anyRequest, permitAll)
|
||||
}
|
||||
httpBasic { }
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
override fun configure(auth: AuthenticationManagerBuilder) {
|
||||
// @formatter:off
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser("user").password("password").roles("USER")
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `security context when require explicit save is true then configure SecurityContextHolderFilter`() {
|
||||
val repository = HttpSessionSecurityContextRepository()
|
||||
val testContext = spring.register(RequireExplicitSaveConfig::class.java)
|
||||
testContext.autowire()
|
||||
val filterChainProxy = testContext.context.getBean(FilterChainProxy::class.java)
|
||||
// @formatter:off
|
||||
val filterTypes = filterChainProxy.getFilters("/").toList()
|
||||
|
||||
assertThat(filterTypes)
|
||||
.anyMatch { it is SecurityContextHolderFilter }
|
||||
.noneMatch { it is SecurityContextPersistenceFilter }
|
||||
// @formatter:on
|
||||
val mvcResult = mvc.perform(SecurityMockMvcRequestBuilders.formLogin()).andReturn()
|
||||
val securityContext = repository
|
||||
.loadContext(HttpRequestResponseHolder(mvcResult.request, mvcResult.response))
|
||||
assertThat(securityContext.authentication).isNotNull
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
open class RequireExplicitSaveConfig : WebSecurityConfigurerAdapter() {
|
||||
override fun configure(http: HttpSecurity) {
|
||||
// @formatter:off
|
||||
http {
|
||||
formLogin { }
|
||||
securityContext {
|
||||
requireExplicitSave = true
|
||||
}
|
||||
}
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
override fun configure(auth: AuthenticationManagerBuilder) {
|
||||
// @formatter:off
|
||||
auth
|
||||
.inMemoryAuthentication()
|
||||
.withUser(PasswordEncodedUser.user())
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue