Add AuthenticationManager to HttpSecurity

Closes gh-10040
This commit is contained in:
Eleftheria Stein 2021-07-07 15:44:42 +02:00
parent 88a3f5ad39
commit 585788ad0a
6 changed files with 311 additions and 3 deletions

View File

@ -143,6 +143,8 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
private FilterOrderRegistration filterOrders = new FilterOrderRegistration();
private AuthenticationManager authenticationManager;
/**
* Creates a new instance
* @param objectPostProcessor the {@link ObjectPostProcessor} that should be used
@ -2722,6 +2724,18 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
return HttpSecurity.this;
}
/**
* Configure the default {@link AuthenticationManager}.
* @param authenticationManager the {@link AuthenticationManager} to use
* @return the {@link HttpSecurity} for further customizations
* @since 5.6
*/
public HttpSecurity authenticationManager(AuthenticationManager authenticationManager) {
Assert.notNull(authenticationManager, "authenticationManager cannot be null");
this.authenticationManager = authenticationManager;
return HttpSecurity.this;
}
@Override
public <C> void setSharedObject(Class<C> sharedType, C object) {
super.setSharedObject(sharedType, object);
@ -2729,7 +2743,12 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
@Override
protected void beforeConfigure() throws Exception {
setSharedObject(AuthenticationManager.class, getAuthenticationRegistry().build());
if (this.authenticationManager != null) {
setSharedObject(AuthenticationManager.class, this.authenticationManager);
}
else {
setSharedObject(AuthenticationManager.class, getAuthenticationRegistry().build());
}
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-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.
@ -17,6 +17,7 @@
package org.springframework.security.config.web.servlet
import org.springframework.context.ApplicationContext
import org.springframework.security.authentication.AuthenticationManager
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
@ -63,11 +64,14 @@ operator fun HttpSecurity.invoke(httpConfiguration: HttpSecurityDsl.() -> Unit)
* @since 5.3
* @param http the [HttpSecurity] which all configurations will be applied to
* @param init the configurations to apply to the provided [HttpSecurity]
* @property authenticationManager the default [AuthenticationManager] to use
*/
@SecurityMarker
class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecurityDsl.() -> Unit) {
private val HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"
var authenticationManager: AuthenticationManager? = null
/**
* Allows configuring the [HttpSecurity] to only be invoked when matching the
* provided pattern.
@ -858,5 +862,6 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
*/
internal fun build() {
init()
authenticationManager?.also { this.http.authenticationManager(authenticationManager) }
}
}

View File

@ -0,0 +1,119 @@
/*
* 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.
* 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.annotation.web.builders;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
public class HttpSecurityAuthenticationManagerTests {
@Autowired
MockMvc mvc;
@Rule
public final SpringTestRule spring = new SpringTestRule();
@Test
public void authenticationManagerWhenConfiguredThenUsed() throws Exception {
this.spring.register(AuthenticationManagerConfig.class).autowire();
given(AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any()))
.willReturn(new TestingAuthenticationToken("user", "test", "ROLE_USER"));
this.mvc.perform(get("/").with(httpBasic("user", "test")));
verify(AuthenticationManagerConfig.AUTHENTICATION_MANAGER).authenticate(any());
}
@Test
public void authenticationManagerWhenBuilderAndAuthenticationManagerConfiguredThenBuilderIgnored()
throws Exception {
this.spring.register(AuthenticationManagerBuilderConfig.class).autowire();
given(AuthenticationManagerBuilderConfig.AUTHENTICATION_MANAGER.authenticate(any()))
.willReturn(new TestingAuthenticationToken("user", "test", "ROLE_USER"));
this.mvc.perform(get("/").with(httpBasic("user", "test")));
verify(AuthenticationManagerBuilderConfig.AUTHENTICATION_MANAGER).authenticate(any());
verifyNoInteractions(AuthenticationManagerBuilderConfig.USER_DETAILS_SERVICE);
}
@EnableWebSecurity
static class AuthenticationManagerConfig extends WebSecurityConfigurerAdapter {
static final AuthenticationManager AUTHENTICATION_MANAGER = mock(AuthenticationManager.class);
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic(withDefaults())
.authenticationManager(AUTHENTICATION_MANAGER);
// @formatter:on
}
}
@EnableWebSecurity
static class AuthenticationManagerBuilderConfig extends WebSecurityConfigurerAdapter {
static final AuthenticationManager AUTHENTICATION_MANAGER = mock(AuthenticationManager.class);
static final UserDetailsService USER_DETAILS_SERVICE = mock(UserDetailsService.class);
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic(withDefaults())
.authenticationManager(AUTHENTICATION_MANAGER);
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(USER_DETAILS_SERVICE);
}
}
}

View File

@ -996,6 +996,24 @@ public class OAuth2ResourceServerConfigurerTests {
verifyBean(AuthenticationProvider.class).authenticate(any(Authentication.class));
}
@Test
public void getWhenDefaultAndCustomJwtAuthenticationManagerThenCustomUsed() throws Exception {
this.spring.register(DefaultAndJwtAuthenticationManagerConfig.class, BasicController.class).autowire();
DefaultAndJwtAuthenticationManagerConfig config = this.spring.getContext()
.getBean(DefaultAndJwtAuthenticationManagerConfig.class);
AuthenticationManager defaultAuthenticationManager = config.defaultAuthenticationManager();
AuthenticationManager jwtAuthenticationManager = config.jwtAuthenticationManager();
given(defaultAuthenticationManager.authenticate(any()))
.willThrow(new RuntimeException("should not interact with default auth manager"));
given(jwtAuthenticationManager.authenticate(any())).willReturn(JWT_AUTHENTICATION_TOKEN);
// @formatter:off
this.mvc.perform(get("/authenticated").with(bearerToken("token")))
.andExpect(status().isOk())
.andExpect(content().string("mock-test-subject"));
// @formatter:on
verify(jwtAuthenticationManager).authenticate(any(Authentication.class));
}
@Test
public void getWhenIntrospectingThenOk() throws Exception {
this.spring.register(RestOperationsConfig.class, OpaqueTokenConfig.class, BasicController.class).autowire();
@ -1054,6 +1072,24 @@ public class OAuth2ResourceServerConfigurerTests {
verifyBean(AuthenticationProvider.class).authenticate(any(Authentication.class));
}
@Test
public void getWhenDefaultAndCustomIntrospectionAuthenticationManagerThenCustomUsed() throws Exception {
this.spring.register(DefaultAndOpaqueTokenAuthenticationManagerConfig.class, BasicController.class).autowire();
DefaultAndOpaqueTokenAuthenticationManagerConfig config = this.spring.getContext()
.getBean(DefaultAndOpaqueTokenAuthenticationManagerConfig.class);
AuthenticationManager defaultAuthenticationManager = config.defaultAuthenticationManager();
AuthenticationManager opaqueTokenAuthenticationManager = config.opaqueTokenAuthenticationManager();
given(defaultAuthenticationManager.authenticate(any()))
.willThrow(new RuntimeException("should not interact with default auth manager"));
given(opaqueTokenAuthenticationManager.authenticate(any())).willReturn(INTROSPECTION_AUTHENTICATION_TOKEN);
// @formatter:off
this.mvc.perform(get("/authenticated").with(bearerToken("token")))
.andExpect(status().isOk())
.andExpect(content().string("mock-test-subject"));
// @formatter:on
verify(opaqueTokenAuthenticationManager).authenticate(any(Authentication.class));
}
@Test
public void getWhenCustomIntrospectionAuthenticationManagerInLambdaThenUsed() throws Exception {
this.spring.register(OpaqueTokenAuthenticationManagerInLambdaConfig.class, BasicController.class).autowire();
@ -2017,6 +2053,39 @@ public class OAuth2ResourceServerConfigurerTests {
}
@EnableWebSecurity
static class DefaultAndJwtAuthenticationManagerConfig extends WebSecurityConfigurerAdapter {
AuthenticationManager defaultAuthenticationManager = mock(AuthenticationManager.class);
AuthenticationManager jwtAuthenticationManager = mock(AuthenticationManager.class);
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authenticationManager(this.defaultAuthenticationManager)
.authorizeRequests((authz) -> authz
.anyRequest().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.jwt((jwt) -> jwt
.authenticationManager(this.jwtAuthenticationManager)
)
);
// @formatter:on
}
AuthenticationManager defaultAuthenticationManager() {
return this.defaultAuthenticationManager;
}
AuthenticationManager jwtAuthenticationManager() {
return this.jwtAuthenticationManager;
}
}
@EnableWebSecurity
static class CustomJwtValidatorConfig extends WebSecurityConfigurerAdapter {
@ -2230,6 +2299,39 @@ public class OAuth2ResourceServerConfigurerTests {
}
@EnableWebSecurity
static class DefaultAndOpaqueTokenAuthenticationManagerConfig extends WebSecurityConfigurerAdapter {
AuthenticationManager defaultAuthenticationManager = mock(AuthenticationManager.class);
AuthenticationManager opaqueTokenAuthenticationManager = mock(AuthenticationManager.class);
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authenticationManager(this.defaultAuthenticationManager)
.authorizeRequests((authz) -> authz
.anyRequest().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.opaqueToken((opaque) -> opaque
.authenticationManager(this.opaqueTokenAuthenticationManager)
)
);
// @formatter:on
}
AuthenticationManager defaultAuthenticationManager() {
return this.defaultAuthenticationManager;
}
AuthenticationManager opaqueTokenAuthenticationManager() {
return this.opaqueTokenAuthenticationManager;
}
}
@EnableWebSecurity
static class OpaqueAndJwtConfig extends WebSecurityConfigurerAdapter {

View File

@ -163,6 +163,12 @@ public class Saml2LoginConfigurerTests {
performSaml2Login("ROLE_AUTH_MANAGER");
}
@Test
public void saml2LoginWhenDefaultAndSamlAuthenticationManagerThenSamlManagerIsUsed() throws Exception {
this.spring.register(Saml2LoginConfigWithDefaultAndCustomAuthenticationManager.class).autowire();
performSaml2Login("ROLE_AUTH_MANAGER");
}
@Test
public void saml2LoginWhenConfiguringAuthenticationDefaultsUsingCustomizerThenTheProviderIsConfigured()
throws Exception {
@ -290,6 +296,24 @@ public class Saml2LoginConfigurerTests {
}
@EnableWebSecurity
@Import(Saml2LoginConfigBeans.class)
static class Saml2LoginConfigWithDefaultAndCustomAuthenticationManager extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authenticationManager(getAuthenticationManagerMock("DEFAULT_AUTH_MANAGER"))
.saml2Login((saml) -> saml
.authenticationManager(getAuthenticationManagerMock("ROLE_AUTH_MANAGER"))
);
super.configure(http);
// @formatter:on
}
}
@EnableWebSecurity
@Import(Saml2LoginConfigBeans.class)
static class Saml2LoginConfigWithAuthenticationDefaultsWithPostProcessor extends WebSecurityConfigurerAdapter {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-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.
@ -16,6 +16,9 @@
package org.springframework.security.config.web.servlet
import io.mockk.every
import io.mockk.mockkObject
import io.mockk.verify
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
@ -23,6 +26,10 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpHeaders
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.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
@ -30,6 +37,7 @@ import org.springframework.security.config.test.SpringTestRule
import org.springframework.security.core.userdetails.User
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.authentication.UsernamePasswordAuthenticationFilter
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter
@ -41,6 +49,7 @@ import org.springframework.security.web.util.matcher.RegexRequestMatcher
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
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
@ -217,6 +226,36 @@ class HttpSecurityDslTests {
}
}
@Test
fun `authentication manager when configured in DSL then used`() {
this.spring.register(AuthenticationManagerConfig::class.java).autowire()
mockkObject(AuthenticationManagerConfig.AUTHENTICATION_MANAGER)
every {
AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any())
} returns TestingAuthenticationToken("user", "test", "ROLE_USER")
val request = MockMvcRequestBuilders.get("/")
.with(httpBasic("user", "password"))
this.mockMvc.perform(request)
verify(exactly = 1) { AuthenticationManagerConfig.AUTHENTICATION_MANAGER.authenticate(any()) }
}
@EnableWebSecurity
open class AuthenticationManagerConfig : WebSecurityConfigurerAdapter() {
companion object {
val AUTHENTICATION_MANAGER: AuthenticationManager = ProviderManager(TestingAuthenticationProvider())
}
override fun configure(http: HttpSecurity) {
http {
authenticationManager = AUTHENTICATION_MANAGER
authorizeRequests {
authorize(anyRequest, authenticated)
}
httpBasic { }
}
}
}
@Test
fun `HTTP security when custom filter configured then custom filter added to filter chain`() {
this.spring.register(CustomFilterConfig::class.java).autowire()