Allow configuring SecurityContextRepository for BasicAuthenticationFilter

Closes gh-12031
This commit is contained in:
Evgeniy Cheban 2023-01-18 12:50:27 +01:00 committed by Steve Riesenberg
parent b237d7ee38
commit 59829321a8
No known key found for this signature in database
GPG Key ID: 5F311AB48A55D521
2 changed files with 53 additions and 2 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -36,6 +36,8 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler; import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.util.matcher.AndRequestMatcher; import org.springframework.security.web.util.matcher.AndRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher; import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import org.springframework.security.web.util.matcher.NegatedRequestMatcher; import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
@ -75,6 +77,7 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
* </ul> * </ul>
* *
* @author Rob Winch * @author Rob Winch
* @author Evgeniy Cheban
* @since 3.2 * @since 3.2
*/ */
public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
@ -91,6 +94,8 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
private BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint(); private BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
private SecurityContextRepository securityContextRepository;
/** /**
* Creates a new instance * Creates a new instance
* @see HttpSecurity#httpBasic() * @see HttpSecurity#httpBasic()
@ -142,6 +147,19 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
return this; return this;
} }
/**
* Specifies a custom {@link SecurityContextRepository} to use for basic
* authentication. The default is {@link RequestAttributeSecurityContextRepository}.
* @param securityContextRepository the custom {@link SecurityContextRepository} to
* use
* @return {@link HttpBasicConfigurer} for additional customization
* @since 6.1
*/
public HttpBasicConfigurer<B> securityContextRepository(SecurityContextRepository securityContextRepository) {
this.securityContextRepository = securityContextRepository;
return this;
}
@Override @Override
public void init(B http) { public void init(B http) {
registerDefaults(http); registerDefaults(http);
@ -195,6 +213,9 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
if (this.authenticationDetailsSource != null) { if (this.authenticationDetailsSource != null) {
basicAuthenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource); basicAuthenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
} }
if (this.securityContextRepository != null) {
basicAuthenticationFilter.setSecurityContextRepository(this.securityContextRepository);
}
RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class); RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
if (rememberMeServices != null) { if (rememberMeServices != null) {
basicAuthenticationFilter.setRememberMeServices(rememberMeServices); basicAuthenticationFilter.setRememberMeServices(rememberMeServices);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -34,6 +34,7 @@ import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextChangedListener; import org.springframework.security.core.context.SecurityContextChangedListener;
import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
@ -42,6 +43,7 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@ -66,6 +68,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* *
* @author Rob Winch * @author Rob Winch
* @author Eleftheria Stein * @author Eleftheria Stein
* @author Evgeniy Cheban
*/ */
@ExtendWith(SpringTestContextExtension.class) @ExtendWith(SpringTestContextExtension.class)
public class HttpBasicConfigurerTests { public class HttpBasicConfigurerTests {
@ -145,6 +148,15 @@ public class HttpBasicConfigurerTests {
verify(listener).securityContextChanged(setAuthentication(UsernamePasswordAuthenticationToken.class)); verify(listener).securityContextChanged(setAuthentication(UsernamePasswordAuthenticationToken.class));
} }
@Test
public void httpBasicWhenUsingCustomSecurityContextRepositoryThenUses() throws Exception {
this.spring.register(CustomSecurityContextRepositoryConfig.class, Users.class, Home.class).autowire();
this.mvc.perform(get("/").with(httpBasic("user", "password"))).andExpect(status().isOk())
.andExpect(content().string("user"));
verify(CustomSecurityContextRepositoryConfig.SECURITY_CONTEXT_REPOSITORY)
.saveContext(any(SecurityContext.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
}
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
static class ObjectPostProcessorConfig { static class ObjectPostProcessorConfig {
@ -321,6 +333,24 @@ public class HttpBasicConfigurerTests {
} }
@Configuration
@EnableWebSecurity
static class CustomSecurityContextRepositoryConfig {
static final SecurityContextRepository SECURITY_CONTEXT_REPOSITORY = mock(SecurityContextRepository.class);
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic()
.securityContextRepository(SECURITY_CONTEXT_REPOSITORY);
// @formatter:on
return http.build();
}
}
@Configuration @Configuration
static class Users { static class Users {