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

Reactive X.509 Authentication

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

The following example shows a reactive x509 security configuration:

  • Java

  • Kotlin

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

In the preceding configuration, when neither principalExtractor nor authenticationManager is provided, defaults are used. The default principal extractor is SubjectX500PrincipalExtractor, 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 a user account with a name extracted by principalExtractor exists and that it is not locked, disabled, or expired.

The following example demonstrates how these defaults can be overridden:

  • Java

  • Kotlin

@Bean
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
	SubjectX500PrincipalExtractor principalExtractor = new SubjectX500PrincipalExtractor();
	principalExtractor.setExtractPrincipalNameFromEmail(true);

	UserDetails user = User
		.withUsername("luke@monkeymachine")
		.password("password")
		.roles("USER")
		.build();

	ReactiveUserDetailsService users = new MapReactiveUserDetailsService(user);
	ReactiveAuthenticationManager authenticationManager = new ReactivePreAuthenticatedAuthenticationManager(users);

	http
		.x509(x509 -> x509
			.principalExtractor(principalExtractor)
			.authenticationManager(authenticationManager)
		)
		.authorizeExchange(exchanges -> exchanges
			.anyExchange().authenticated()
		);
	return http.build();
}
@Bean
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
    val extractor = SubjectX500PrincipalExtractor()
    extractor.setExtractPrincipalNameFromEmail(true)

    val user = User
        .withUsername("luke@monkeymachine")
        .password("password")
        .roles("USER")
        .build()

    val users: ReactiveUserDetailsService = MapReactiveUserDetailsService(user)
    val authentication: ReactiveAuthenticationManager = ReactivePreAuthenticatedAuthenticationManager(users)

    return http {
        x509 {
            principalExtractor = extractor
            authenticationManager = authentication
        }
        authorizeExchange {
            authorize(anyExchange, authenticated)
        }
    }
}

In the previous example, a username is extracted from the emailAddress field of a client certificate instead of CN, and account lookup uses a custom ReactiveAuthenticationManager instance.

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