Skip to content

Commit e3add59

Browse files
committed
Update x509 Reference
- Use include-code - Demo how to customize SubjectX500PrincipalExtractor
1 parent 7bf2730 commit e3add59

File tree

17 files changed

+1120
-116
lines changed

17 files changed

+1120
-116
lines changed

docs/antora.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ asciidoc:
1818
gh-url: "https://github.com/spring-projects/spring-security/tree/{gh-tag}"
1919
include-java: 'example$docs-src/test/java/org/springframework/security/docs'
2020
include-kotlin: 'example$docs-src/test/kotlin/org/springframework/security/kt/docs'
21+
include-xml: 'example$docs-src/test/resources/org/springframework/security/docs'

docs/modules/ROOT/pages/reactive/authentication/x509.adoc

Lines changed: 6 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -5,98 +5,16 @@ Similar to xref:servlet/authentication/x509.adoc#servlet-x509[Servlet X.509 auth
55

66
The following example shows a reactive x509 security configuration:
77

8-
[tabs]
9-
======
10-
Java::
11-
+
12-
[source,java,role="primary"]
13-
----
14-
@Bean
15-
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
16-
http
17-
.x509(withDefaults())
18-
.authorizeExchange(exchanges -> exchanges
19-
.anyExchange().permitAll()
20-
);
21-
return http.build();
22-
}
23-
----
8+
include-code::./DefaultX509Configuration[tag=springSecurity,indent=0]
249

25-
Kotlin::
26-
+
27-
[source,kotlin,role="secondary"]
28-
----
29-
@Bean
30-
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
31-
return http {
32-
x509 { }
33-
authorizeExchange {
34-
authorize(anyExchange, authenticated)
35-
}
36-
}
37-
}
38-
----
39-
======
40-
41-
In the preceding configuration, when neither `principalExtractor` nor `authenticationManager` is provided, defaults are 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 a user account with a name extracted by `principalExtractor` exists and that it is not locked, disabled, or expired.
10+
In the preceding configuration, when neither `principalExtractor` nor `authenticationManager` is provided, defaults are used.
11+
The default principal extractor is `SubjectX500PrincipalExtractor`, which extracts the CN (common name) field from a certificate provided by a client.
12+
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.
4213

4314
The following example demonstrates how these defaults can be overridden:
4415

45-
[tabs]
46-
======
47-
Java::
48-
+
49-
[source,java,role="primary"]
50-
----
51-
@Bean
52-
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
53-
SubjectDnX509PrincipalExtractor principalExtractor =
54-
new SubjectDnX509PrincipalExtractor();
55-
56-
principalExtractor.setSubjectDnRegex("OU=(.*?)(?:,|$)");
57-
58-
ReactiveAuthenticationManager authenticationManager = authentication -> {
59-
authentication.setAuthenticated("Trusted Org Unit".equals(authentication.getName()));
60-
return Mono.just(authentication);
61-
};
62-
63-
http
64-
.x509(x509 -> x509
65-
.principalExtractor(principalExtractor)
66-
.authenticationManager(authenticationManager)
67-
)
68-
.authorizeExchange(exchanges -> exchanges
69-
.anyExchange().authenticated()
70-
);
71-
return http.build();
72-
}
73-
----
74-
75-
Kotlin::
76-
+
77-
[source,kotlin,role="secondary"]
78-
----
79-
@Bean
80-
fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? {
81-
val customPrincipalExtractor = SubjectDnX509PrincipalExtractor()
82-
customPrincipalExtractor.setSubjectDnRegex("OU=(.*?)(?:,|$)")
83-
val customAuthenticationManager = ReactiveAuthenticationManager { authentication: Authentication ->
84-
authentication.isAuthenticated = "Trusted Org Unit" == authentication.name
85-
Mono.just(authentication)
86-
}
87-
return http {
88-
x509 {
89-
principalExtractor = customPrincipalExtractor
90-
authenticationManager = customAuthenticationManager
91-
}
92-
authorizeExchange {
93-
authorize(anyExchange, authenticated)
94-
}
95-
}
96-
}
97-
----
98-
======
16+
include-code::./CustomX509Configuration[tag=springSecurity,indent=0]
9917

100-
In the previous 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 is authenticated.
18+
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.
10119

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

docs/modules/ROOT/pages/servlet/authentication/x509.adoc

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,37 +14,27 @@ You should get this working before trying it out with Spring Security.
1414
The Spring Security X.509 module extracts the certificate by using a filter.
1515
It maps the certificate to an application user and loads that user's set of granted authorities for use with the standard Spring Security infrastructure.
1616

17-
17+
[[servlet-x509-config]]
1818
== Adding X.509 Authentication to Your Web Application
19-
Enabling X.509 client authentication is very straightforward.
20-
To do so, add the `<x509/>` element to your http security namespace configuration:
2119

22-
[source,xml]
23-
----
24-
<http>
25-
...
26-
<x509 subject-principal-regex="CN=(.*?)," user-service-ref="userService"/>;
27-
</http>
28-
----
20+
Similar to xref:reactive/authentication/x509.adoc[Reactive X.509 authentication], the servlet x509 authentication filter allows extracting an authentication token from a certificate provided by a client.
21+
22+
The following example shows a reactive x509 security configuration:
23+
24+
include-code::./DefaultX509Configuration[tag=springSecurity,indent=0]
25+
26+
In the preceding configuration, when neither `principalExtractor` nor `authenticationManager` is provided, defaults are used.
27+
The default principal extractor is `SubjectX500PrincipalExtractor`, which extracts the CN (common name) field from a certificate provided by a client.
28+
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.
29+
30+
The following example demonstrates how these defaults can be overridden:
31+
32+
include-code::./CustomX509Configuration[tag=springSecurity,indent=0]
33+
34+
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.
35+
36+
For an example of configuring Netty and `WebClient` or `curl` command-line tool to use mutual TLS and enable X.509 authentication, see https://github.com/spring-projects/spring-security-samples/tree/main/servlet/java-configuration/authentication/x509.
2937

30-
The element has two optional attributes:
31-
32-
* `subject-principal-regex`.
33-
The regular expression used to extract a username from the certificate's subject name.
34-
The default value is shown in the preceding listing.
35-
This is the username that is passed to the `UserDetailsService` to load the authorities for the user.
36-
* `user-service-ref`.
37-
This is the bean ID of the `UserDetailsService` to be used with X.509.
38-
It is not needed if there is only one defined in your application context.
39-
40-
The `subject-principal-regex` should contain a single group.
41-
For example, the default expression (`CN=(.*?)`) matches the common name field.
42-
So, if the subject name in the certificate is "CN=Jimi Hendrix, OU=...", this gives a user name of "Jimi Hendrix".
43-
The matches are case insensitive.
44-
So "emailAddress=(+.*?+)," matches "[email protected],CN=...", giving a user name "[email protected]".
45-
If the client presents a certificate and a valid username is successfully extracted, there should be a valid `Authentication` object in the security context.
46-
If no certificate is found or no corresponding user could be found, the security context remains empty.
47-
This means that you can use X.509 authentication with other options, such as a form-based login.
4838

4939
[[x509-ssl-config]]
5040
== Setting up SSL in Tomcat
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2002-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.docs.reactive.authentication.reactivex509;
18+
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.security.authentication.ReactiveAuthenticationManager;
22+
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
23+
import org.springframework.security.config.web.server.ServerHttpSecurity;
24+
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
25+
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
26+
import org.springframework.security.core.userdetails.User;
27+
import org.springframework.security.core.userdetails.UserDetails;
28+
import org.springframework.security.web.authentication.preauth.x509.SubjectX500PrincipalExtractor;
29+
import org.springframework.security.web.server.SecurityWebFilterChain;
30+
import org.springframework.security.web.server.authentication.ReactivePreAuthenticatedAuthenticationManager;
31+
import org.springframework.web.reactive.config.EnableWebFlux;
32+
33+
/**
34+
* Demonstrates custom configuration for x509 reactive configuration.
35+
*
36+
* @author Rob Winch
37+
*/
38+
@Configuration(proxyBeanMethods = false)
39+
@EnableWebFluxSecurity
40+
@EnableWebFlux
41+
public class CustomX509Configuration {
42+
43+
// tag::springSecurity[]
44+
@Bean
45+
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
46+
SubjectX500PrincipalExtractor principalExtractor = new SubjectX500PrincipalExtractor();
47+
principalExtractor.setExtractPrincipalNameFromEmail(true);
48+
49+
// @formatter:off
50+
UserDetails user = User
51+
.withUsername("luke@monkeymachine")
52+
.password("password")
53+
.roles("USER")
54+
.build();
55+
// @formatter:on
56+
57+
ReactiveUserDetailsService users = new MapReactiveUserDetailsService(user);
58+
ReactiveAuthenticationManager authenticationManager = new ReactivePreAuthenticatedAuthenticationManager(users);
59+
60+
// @formatter:off
61+
http
62+
.x509(x509 -> x509
63+
.principalExtractor(principalExtractor)
64+
.authenticationManager(authenticationManager)
65+
)
66+
.authorizeExchange(exchanges -> exchanges
67+
.anyExchange().authenticated()
68+
);
69+
// @formatter:on
70+
return http.build();
71+
}
72+
// end::springSecurity[]
73+
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2002-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.docs.reactive.authentication.reactivex509;
18+
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.security.config.Customizer;
22+
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
23+
import org.springframework.security.config.web.server.ServerHttpSecurity;
24+
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
25+
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
26+
import org.springframework.security.core.userdetails.User;
27+
import org.springframework.security.core.userdetails.UserDetails;
28+
import org.springframework.security.web.server.SecurityWebFilterChain;
29+
import org.springframework.web.reactive.config.EnableWebFlux;
30+
31+
/**
32+
* Demonstrates custom configuration for x509 reactive configuration.
33+
*
34+
* @author Rob Winch
35+
*/
36+
@Configuration(proxyBeanMethods = false)
37+
@EnableWebFluxSecurity
38+
@EnableWebFlux
39+
public class DefaultX509Configuration {
40+
41+
// tag::springSecurity[]
42+
@Bean
43+
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
44+
// @formatter:off
45+
http
46+
.x509(Customizer.withDefaults())
47+
.authorizeExchange(exchanges -> exchanges
48+
.anyExchange().authenticated()
49+
);
50+
// @formatter:on
51+
return http.build();
52+
}
53+
// end::springSecurity[]
54+
55+
@Bean
56+
ReactiveUserDetailsService userDetailsService() {
57+
// @formatter:off
58+
UserDetails user = User
59+
.withUsername("rod")
60+
.password("password")
61+
.roles("USER")
62+
.build();
63+
// @formatter:on
64+
65+
return new MapReactiveUserDetailsService(user);
66+
}
67+
}

0 commit comments

Comments
 (0)