Ability to configure authenticationDetailsSource in AnonymousConfigurer

Closes gh-17831

Signed-off-by: DingHao <dh.hiekn@gmail.com>
This commit is contained in:
DingHao 2025-09-12 17:06:25 +08:00
parent 10935632ee
commit 57d9678b4b
2 changed files with 57 additions and 0 deletions

View File

@ -19,7 +19,10 @@ package org.springframework.security.config.annotation.web.configurers;
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 @@ import org.springframework.security.web.authentication.AnonymousAuthenticationFi
* other than applying this {@link SecurityConfigurer}.
*
* @author Rob Winch
* @author DingHao
* @since 3.2
*/
public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>>
@ -50,6 +54,8 @@ public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>>
private AnonymousAuthenticationFilter authenticationFilter;
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
private Object principal = "anonymousUser";
private List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS");
@ -142,6 +148,19 @@ public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>>
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<H> authenticationDetailsSource(
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
this.authenticationDetailsSource = authenticationDetailsSource;
return this;
}
@Override
public void init(H http) {
if (this.authenticationProvider == null) {
@ -156,6 +175,9 @@ public final class AnonymousConfigurer<H extends HttpSecurityBuilder<H>>
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);

View File

@ -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.beans.factory.annotation.Autowired;
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.GetMapping;
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 class AnonymousConfigurerTests {
this.mockMvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("myAnonymousUser"));
}
@Test
public void anonymousAuthenticationWhenUsingAuthenticationDetailsSourceRefThenMatchesNamespace() throws Exception {
this.spring.register(AuthenticationDetailsSourceAnonymousConfig.class).autowire();
AuthenticationDetailsSource<HttpServletRequest, ?> source = this.spring.getContext()
.getBean(AuthenticationDetailsSource.class);
this.mockMvc.perform(get("/"));
verify(source).buildDetails(any(HttpServletRequest.class));
}
@Configuration
@EnableWebSecurity
@EnableWebMvc
static class AuthenticationDetailsSourceAnonymousConfig {
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = mock(
AuthenticationDetailsSource.class);
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.anonymous((anonymous) -> anonymous.authenticationDetailsSource(this.authenticationDetailsSource))
.build();
}
@Bean
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource() {
return this.authenticationDetailsSource;
}
}
@Configuration
@EnableWebSecurity
@EnableWebMvc