diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurer.java index ab9770a13e..505f6f35a5 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurer.java @@ -19,7 +19,10 @@ import java.util.List; import java.util.UUID; +import jakarta.servlet.http.HttpServletRequest; + import org.springframework.security.authentication.AnonymousAuthenticationProvider; +import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.SecurityConfigurer; @@ -39,6 +42,7 @@ * other than applying this {@link SecurityConfigurer}. * * @author Rob Winch + * @author DingHao * @since 3.2 */ public final class AnonymousConfigurer> @@ -50,6 +54,8 @@ public final class AnonymousConfigurer> private AnonymousAuthenticationFilter authenticationFilter; + private AuthenticationDetailsSource authenticationDetailsSource; + private Object principal = "anonymousUser"; private List authorities = AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"); @@ -142,6 +148,19 @@ public AnonymousConfigurer authenticationFilter(AnonymousAuthenticationFilter return this; } + /** + * Specifies a custom {@link AuthenticationDetailsSource} to use for anonymous + * authentication. + * @param authenticationDetailsSource the custom {@link AuthenticationDetailsSource} + * to use + * @return {@link AnonymousConfigurer} for additional customization + */ + public AnonymousConfigurer authenticationDetailsSource( + AuthenticationDetailsSource authenticationDetailsSource) { + this.authenticationDetailsSource = authenticationDetailsSource; + return this; + } + @Override public void init(H http) { if (this.authenticationProvider == null) { @@ -156,6 +175,9 @@ public void configure(H http) { if (this.authenticationFilter == null) { this.authenticationFilter = new AnonymousAuthenticationFilter(getKey(), this.principal, this.authorities); } + if (this.authenticationDetailsSource != null) { + this.authenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource); + } this.authenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy()); this.authenticationFilter.afterPropertiesSet(); http.addFilter(this.authenticationFilter); diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurerTests.java index 4a7b20713c..8452bffbae 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurerTests.java @@ -16,6 +16,7 @@ package org.springframework.security.config.annotation.web.configurers; +import jakarta.servlet.http.HttpServletRequest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -23,6 +24,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -39,6 +41,8 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.springframework.security.config.Customizer.withDefaults; import static org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.setAuthentication; @@ -101,6 +105,37 @@ public void shouldReturnMyCustomAnonymousConfig() throws Exception { this.mockMvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("myAnonymousUser")); } + @Test + public void anonymousAuthenticationWhenUsingAuthenticationDetailsSourceRefThenMatchesNamespace() throws Exception { + this.spring.register(AuthenticationDetailsSourceAnonymousConfig.class).autowire(); + AuthenticationDetailsSource source = this.spring.getContext() + .getBean(AuthenticationDetailsSource.class); + this.mockMvc.perform(get("/")); + verify(source).buildDetails(any(HttpServletRequest.class)); + } + + @Configuration + @EnableWebSecurity + @EnableWebMvc + static class AuthenticationDetailsSourceAnonymousConfig { + + AuthenticationDetailsSource authenticationDetailsSource = mock( + AuthenticationDetailsSource.class); + + @Bean + SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + return http + .anonymous((anonymous) -> anonymous.authenticationDetailsSource(this.authenticationDetailsSource)) + .build(); + } + + @Bean + AuthenticationDetailsSource authenticationDetailsSource() { + return this.authenticationDetailsSource; + } + + } + @Configuration @EnableWebSecurity @EnableWebMvc