You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebFlux中如何实现等效于Servlet的.x509().subjectPrincipalRegex(...)证书认证?

WebFlux Equivalent of .x509().subjectPrincipalRegex(...) for Client Certificate Authentication

Great question! In Spring WebFlux's reactive security stack, there’s no direct out-of-the-box .x509() method like in the Servlet-based HttpSecurity. But we can build an equivalent solution to extract the client certificate’s subject as a username and feed it into your ReactiveUserDetailsService using custom components. Here’s how to do it step by step:

Step 1: Configure SSL to Require Client Certificates

First, ensure your server is set up to request and validate client certificates. Add this to your application.yml (or .properties):

server:
  ssl:
    enabled: true
    key-store: classpath:server.jks
    key-store-password: your-server-keystore-pass
    trust-store: classpath:truststore.jks
    trust-store-password: your-truststore-pass
    client-auth: required # Forces clients to present a valid certificate

Step 2: Create a Custom X509 Authentication Converter

This component will extract the client certificate from the request, parse its subject DN using your custom regex (mirroring subjectPrincipalRegex), and convert it into an authentication token.

import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.net.ssl.SSLSession;
import java.security.cert.X509Certificate;

public class X509ServerAuthenticationConverter implements ServerAuthenticationConverter {

    // Match this to your desired subject regex (same as .subjectPrincipalRegex() in Servlet)
    private String subjectPrincipalRegex = "CN=(.*?)(?:,|$)";

    @Override
    public Mono<Authentication> convert(ServerWebExchange exchange) {
        return Mono.fromCallable(() -> exchange.getRequest().getSslInfo())
                .filter(sslInfo -> sslInfo.getSession() != null)
                .map(sslInfo -> sslInfo.getSession().getPeerCertificates())
                .filter(certs -> certs.length > 0 && certs[0] instanceof X509Certificate)
                .map(certs -> (X509Certificate) certs[0])
                .map(this::extractUsernameFromSubjectDn)
                .map(username -> new UsernamePasswordAuthenticationToken(username, null, null));
    }

    private String extractUsernameFromSubjectDn(X509Certificate cert) {
        String subjectDn = cert.getSubjectDN().getName();
        java.util.regex.Matcher matcher = java.util.regex.Pattern.compile(subjectPrincipalRegex).matcher(subjectDn);
        return matcher.find() ? matcher.group(1) : subjectDn;
    }

    // Setter to customize the regex externally
    public void setSubjectPrincipalRegex(String subjectPrincipalRegex) {
        this.subjectPrincipalRegex = subjectPrincipalRegex;
    }
}

Step 3: Integrate with ServerHttpSecurity

Wire up the custom converter into your security filter chain, and link it to your ReactiveUserDetailsService via an authentication manager:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;

@Configuration
@EnableWebFluxSecurity
public class WebFluxSecurityConfig {

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http,
                                                        ReactiveUserDetailsService userDetailsService) {
        // Create an authentication manager that uses your ReactiveUserDetailsService
        ReactiveAuthenticationManager authManager =
                new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);

        // Build the X509 authentication filter with our custom converter
        AuthenticationWebFilter x509AuthFilter = new AuthenticationWebFilter(authManager);
        X509ServerAuthenticationConverter x509Converter = new X509ServerAuthenticationConverter();
        // Set your custom regex here (e.g., to extract UID instead of CN)
        x509Converter.setSubjectPrincipalRegex("UID=(.*?)(?:,|$)");
        x509AuthFilter.setServerAuthenticationConverter(x509Converter);

        // Configure the security chain
        return http
                .csrf(ServerHttpSecurity.CsrfSpec::disable)
                .authorizeExchange(exchanges -> exchanges
                        .anyExchange().authenticated())
                .addFilterAt(x509AuthFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                .build();
    }
}

Key Notes

  • Regex Flexibility: Just like .subjectPrincipalRegex(), you can adjust the regex to extract any part of the certificate’s subject DN (e.g., CN, UID, email) based on your needs.
  • Alternative to Regex: For more robust parsing, you can use javax.security.auth.x500.X500Principal's methods to extract specific attributes instead of regex, avoiding pattern matching errors.
  • Authentication Flow: The filter will extract the username from the certificate, pass it to ReactiveUserDetailsService to load user details, and validate the user’s existence/authorities just like standard authentication.

内容的提问来源于stack exchange,提问作者Stmated

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:22:17