feat: Add option to specify a custom ServerAuthenticationConverter for x509()

Signed-off-by: blake_bauman <blake_bauman@apple.com>
This commit is contained in:
blake_bauman 2025-06-27 02:05:59 -07:00
parent 00ead7f24d
commit ead384569d
Failed to extract signature
2 changed files with 39 additions and 1 deletions

View File

@ -3214,6 +3214,8 @@ public class ServerHttpSecurity {
private ReactiveAuthenticationManager authenticationManager; private ReactiveAuthenticationManager authenticationManager;
private ServerAuthenticationConverter serverAuthenticationConverter;
private X509Spec() { private X509Spec() {
} }
@ -3227,11 +3229,17 @@ public class ServerHttpSecurity {
return this; return this;
} }
public X509Spec serverAuthenticationConverter(ServerAuthenticationConverter serverAuthenticationConverter) {
this.serverAuthenticationConverter = serverAuthenticationConverter;
return this;
}
protected void configure(ServerHttpSecurity http) { protected void configure(ServerHttpSecurity http) {
ReactiveAuthenticationManager authenticationManager = getAuthenticationManager(); ReactiveAuthenticationManager authenticationManager = getAuthenticationManager();
X509PrincipalExtractor principalExtractor = getPrincipalExtractor(); X509PrincipalExtractor principalExtractor = getPrincipalExtractor();
ServerAuthenticationConverter converter = getServerAuthenticationConverter(principalExtractor);
AuthenticationWebFilter filter = new AuthenticationWebFilter(authenticationManager); AuthenticationWebFilter filter = new AuthenticationWebFilter(authenticationManager);
filter.setServerAuthenticationConverter(new ServerX509AuthenticationConverter(principalExtractor)); filter.setServerAuthenticationConverter(serverAuthenticationConverter);
http.addFilterAt(filter, SecurityWebFiltersOrder.AUTHENTICATION); http.addFilterAt(filter, SecurityWebFiltersOrder.AUTHENTICATION);
} }
@ -3250,6 +3258,13 @@ public class ServerHttpSecurity {
return new ReactivePreAuthenticatedAuthenticationManager(userDetailsService); return new ReactivePreAuthenticatedAuthenticationManager(userDetailsService);
} }
private ServerAuthenticationConverter getServerAuthenticationConverter(X509PrincipalExtractor extractor) {
if (this.serverAuthenticationConverter != null) {
return this.serverAuthenticationConverter;
}
return new ServerX509AuthenticationConverter(extractor);
}
} }
public final class OAuth2LoginSpec { public final class OAuth2LoginSpec {

View File

@ -60,6 +60,7 @@ import org.springframework.security.web.server.authentication.AnonymousAuthentic
import org.springframework.security.web.server.authentication.DelegatingServerAuthenticationSuccessHandler; import org.springframework.security.web.server.authentication.DelegatingServerAuthenticationSuccessHandler;
import org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint; import org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint;
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint; import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler; import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler; import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
import org.springframework.security.web.server.authentication.ServerX509AuthenticationConverter; import org.springframework.security.web.server.authentication.ServerX509AuthenticationConverter;
@ -497,6 +498,17 @@ public class ServerHttpSecurityTests {
assertThat(x509WebFilter).isNotNull(); assertThat(x509WebFilter).isNotNull();
} }
@Test
public void x509WithConverterAndNoExtractorThenAddsX509Filter() {
ServerAuthenticationConverter mockConverter = mock(ServerAuthenticationConverter.class);
this.http.x509((x509) -> x509.serverAuthenticationConverter(mockConverter));
SecurityWebFilterChain securityWebFilterChain = this.http.build();
WebFilter x509WebFilter = securityWebFilterChain.getWebFilters()
.filter(filter -> matchesX509Converter(filter, mockConverter))
.blockFirst();
assertThat(x509WebFilter).isNotNull();
}
@Test @Test
public void addsX509FilterWhenX509AuthenticationIsConfiguredWithDefaults() { public void addsX509FilterWhenX509AuthenticationIsConfiguredWithDefaults() {
this.http.x509(withDefaults()); this.http.x509(withDefaults());
@ -769,6 +781,17 @@ public class ServerHttpSecurityTests {
} }
} }
private boolean matchesX509Converter(WebFilter filter, ServerAuthenticationConverter expectedConverter) {
try {
Object converter = ReflectionTestUtils.getField(filter, "authenticationConverter");
return converter.equals(expectedConverter);
}
catch (IllegalArgumentException ex) {
// field doesn't exist
return false;
}
}
private <T extends WebFilter> Optional<T> getWebFilter(SecurityWebFilterChain filterChain, Class<T> filterClass) { private <T extends WebFilter> Optional<T> getWebFilter(SecurityWebFilterChain filterChain, Class<T> filterClass) {
return (Optional<T>) filterChain.getWebFilters() return (Optional<T>) filterChain.getWebFilters()
.filter(Objects::nonNull) .filter(Objects::nonNull)