This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Security 6.4.2!

Reactive X.509 Authentication

Similar to Servlet X.509 authentication, reactive x509 authentication filter allows extracting an authentication token from a certificate provided by a client.

Below is an example of a reactive x509 security configuration:

  • Java

  • Kotlin

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
	http
		.x509(withDefaults())
		.authorizeExchange(exchanges -> exchanges
		    .anyExchange().permitAll()
		);
	return http.build();
}
@Bean
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
    return http {
        x509 { }
        authorizeExchange {
            authorize(anyExchange, authenticated)
        }
    }
}

In the configuration above, when neither principalExtractor nor authenticationManager is provided defaults will be used. The default principal extractor is SubjectDnX509PrincipalExtractor which extracts the CN (common name) field from a certificate provided by a client. The default authentication manager is ReactivePreAuthenticatedAuthenticationManager which performs user account validation, checking that user account with a name extracted by principalExtractor exists and it is not locked, disabled, or expired.

The next example demonstrates how these defaults can be overridden.

  • Java

  • Kotlin

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
	SubjectDnX509PrincipalExtractor principalExtractor =
	        new SubjectDnX509PrincipalExtractor();

	principalExtractor.setSubjectDnRegex("OU=(.*?)(?:,|$)");

	ReactiveAuthenticationManager authenticationManager = authentication -> {
		authentication.setAuthenticated("Trusted Org Unit".equals(authentication.getName()));
		return Mono.just(authentication);
	};

	http
		.x509(x509 -> x509
		    .principalExtractor(principalExtractor)
		    .authenticationManager(authenticationManager)
		)
		.authorizeExchange(exchanges -> exchanges
		    .anyExchange().authenticated()
		);
	return http.build();
}
@Bean
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? {
    val customPrincipalExtractor = SubjectDnX509PrincipalExtractor()
    customPrincipalExtractor.setSubjectDnRegex("OU=(.*?)(?:,|$)")
    val customAuthenticationManager = ReactiveAuthenticationManager { authentication: Authentication ->
        authentication.isAuthenticated = "Trusted Org Unit" == authentication.name
        Mono.just(authentication)
    }
    return http {
        x509 {
            principalExtractor = customPrincipalExtractor
            authenticationManager = customAuthenticationManager
        }
        authorizeExchange {
            authorize(anyExchange, authenticated)
        }
    }
}

In this example, a username is extracted from the OU field of a client certificate instead of CN, and account lookup using ReactiveUserDetailsService is not performed at all. Instead, if the provided certificate issued to an OU named "Trusted Org Unit", a request will be authenticated.

For an example of configuring Netty and WebClient or curl command-line tool to use mutual TLS and enable X.509 authentication, please refer to github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration/authentication/x509.