diff --git a/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java b/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java
index f997e5fa5e..55efc57a17 100644
--- a/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java
+++ b/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java
@@ -521,11 +521,23 @@ final class AuthenticationConfigBuilder {
filterBuilder.addPropertyValue("authenticationManager", authManager);
filterBuilder.addPropertyValue("securityContextHolderStrategy",
authenticationFilterSecurityContextHolderStrategyRef);
- String regex = x509Elt.getAttribute("subject-principal-regex");
- if (StringUtils.hasText(regex)) {
+ String principalExtractorRef = x509Elt.getAttribute("principal-extractor-ref");
+ String subjectPrincipalRegex = x509Elt.getAttribute("subject-principal-regex");
+ boolean hasPrincipalExtractorRef = StringUtils.hasText(principalExtractorRef);
+ boolean hasSubjectPrincipalRegex = StringUtils.hasText(subjectPrincipalRegex);
+ if (hasPrincipalExtractorRef && hasSubjectPrincipalRegex) {
+ this.pc.getReaderContext()
+ .error("The attribute 'principal-extractor-ref' cannot be used together with the 'subject-principal-regex' attribute within <"
+ + Elements.X509 + ">", this.pc.extractSource(x509Elt));
+ }
+ if (hasPrincipalExtractorRef) {
+ RuntimeBeanReference principalExtractor = new RuntimeBeanReference(principalExtractorRef);
+ filterBuilder.addPropertyValue("principalExtractor", principalExtractor);
+ }
+ if (hasSubjectPrincipalRegex) {
BeanDefinitionBuilder extractor = BeanDefinitionBuilder
.rootBeanDefinition(SubjectDnX509PrincipalExtractor.class);
- extractor.addPropertyValue("subjectDnRegex", regex);
+ extractor.addPropertyValue("subjectDnRegex", subjectPrincipalRegex);
filterBuilder.addPropertyValue("principalExtractor", extractor.getBeanDefinition());
}
injectAuthenticationDetailsSource(x509Elt, filterBuilder);
diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-7.0.rnc b/config/src/main/resources/org/springframework/security/config/spring-security-7.0.rnc
index bbf8622dfe..d9a8d4f9d5 100644
--- a/config/src/main/resources/org/springframework/security/config/spring-security-7.0.rnc
+++ b/config/src/main/resources/org/springframework/security/config/spring-security-7.0.rnc
@@ -1053,6 +1053,9 @@ x509.attlist &=
x509.attlist &=
## Reference to an AuthenticationDetailsSource which will be used by the authentication filter
attribute authentication-details-source-ref {xsd:token}?
+x509.attlist &=
+ ## Reference to an X509PrincipalExtractor which will be used by the authentication filter
+ attribute principal-extractor-ref {xsd:token}?
jee =
## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication.
diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-7.0.xsd b/config/src/main/resources/org/springframework/security/config/spring-security-7.0.xsd
index 2e3d6cf275..4ff414800b 100644
--- a/config/src/main/resources/org/springframework/security/config/spring-security-7.0.xsd
+++ b/config/src/main/resources/org/springframework/security/config/spring-security-7.0.xsd
@@ -2917,6 +2917,12 @@
+
+
+ Reference to an X509PrincipalExtractor which will be used by the authentication filter
+
+
+
diff --git a/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java b/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java
index 5221350cde..10927906ea 100644
--- a/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java
+++ b/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java
@@ -21,6 +21,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.security.AccessController;
import java.security.Principal;
+import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
@@ -91,6 +92,7 @@ import org.springframework.security.web.authentication.AnonymousAuthenticationFi
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
+import org.springframework.security.web.authentication.preauth.x509.X509TestUtils;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
@@ -398,6 +400,27 @@ public class MiscHttpConfigTests {
.containsSubsequence(CsrfFilter.class, X509AuthenticationFilter.class, ExceptionTranslationFilter.class);
}
+ @Test
+ public void getWhenUsingX509PrincipalExtractorRef() throws Exception {
+ this.spring.configLocations(xml("X509PrincipalExtractorRef")).autowire();
+ X509Certificate certificate = X509TestUtils.buildTestCertificate();
+ RequestPostProcessor x509 = x509(certificate);
+ // @formatter:off
+ this.mvc.perform(get("/protected").with(x509))
+ .andExpect(status().isOk());
+ // @formatter:on
+ }
+
+ @Test
+ public void getWhenUsingX509PrincipalExtractorRefAndSubjectPrincipalRegex() throws Exception {
+ String xmlResourceName = "X509PrincipalExtractorRefAndSubjectPrincipalRegex";
+ // @formatter:off
+ assertThatExceptionOfType(BeanDefinitionParsingException.class)
+ .isThrownBy(() -> this.spring.configLocations(xml(xmlResourceName)).autowire())
+ .withMessage("Configuration problem: The attribute 'principal-extractor-ref' cannot be used together with the 'subject-principal-regex' attribute within \n" + "Offending resource: class path resource [org/springframework/security/config/http/MiscHttpConfigTests-X509PrincipalExtractorRefAndSubjectPrincipalRegex.xml]");
+ // @formatter:on
+ }
+
@Test
public void getWhenUsingX509AndPropertyPlaceholderThenSubjectPrincipalRegexIsConfigured() throws Exception {
System.setProperty("subject_principal_regex", "OU=(.*?)(?:,|$)");
diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-X509PrincipalExtractorRef.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-X509PrincipalExtractorRef.xml
new file mode 100644
index 0000000000..51a3a1bbf1
--- /dev/null
+++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-X509PrincipalExtractorRef.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-X509PrincipalExtractorRefAndSubjectPrincipalRegex.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-X509PrincipalExtractorRefAndSubjectPrincipalRegex.xml
new file mode 100644
index 0000000000..1fc7e0f12c
--- /dev/null
+++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-X509PrincipalExtractorRefAndSubjectPrincipalRegex.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc b/docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc
index 8979d5ad29..555a1b4323 100644
--- a/docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc
+++ b/docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc
@@ -2218,6 +2218,10 @@ A `PreAuthenticatedAuthenticationProvider` will also be created which delegates
* **authentication-details-source-ref**
A reference to an `AuthenticationDetailsSource`
+[[nsa-x509-principal-extractor-ref]]
+* **principal-extractor-ref**
+Reference to an `X509PrincipalExtractor` which will be used by the authentication filter.
+
[[nsa-x509-subject-principal-regex]]
* **subject-principal-regex**