ソースを参照

Allow configuration of x509 through nested builder

Issue: gh-5557
Eleftheria Stein 6 年 前
コミット
ae9eb6f56b

+ 36 - 0
config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

@@ -861,6 +861,42 @@ public final class HttpSecurity extends
 		return getOrApply(new X509Configurer<>());
 	}
 
+	/**
+	 * Configures X509 based pre authentication.
+	 *
+	 * <h2>Example Configuration</h2>
+	 *
+	 * The following configuration will attempt to extract the username from the X509
+	 * certificate. Remember that the Servlet Container will need to be configured to
+	 * request client certificates in order for this to work.
+	 *
+	 * <pre>
+	 * &#064;Configuration
+	 * &#064;EnableWebSecurity
+	 * public class X509SecurityConfig extends WebSecurityConfigurerAdapter {
+	 *
+	 * 	&#064;Override
+	 * 	protected void configure(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 			.authorizeRequests()
+	 * 				.antMatchers(&quot;/**&quot;)
+	 * 				.hasRole(&quot;USER&quot;)
+	 * 				.and()
+	 * 			.x509(withDefaults());
+	 * 	}
+	 * }
+	 * </pre>
+	 *
+	 * @param x509Customizer the {@link Customizer} to provide more options for
+	 * the {@link X509Configurer}
+	 * @return the {@link HttpSecurity} for further customizations
+	 * @throws Exception
+	 */
+	public HttpSecurity x509(Customizer<X509Configurer<HttpSecurity>> x509Customizer) throws Exception {
+		x509Customizer.customize(getOrApply(new X509Configurer<>()));
+		return HttpSecurity.this;
+	}
+
 	/**
 	 * Allows configuring of Remember Me authentication.
 	 *

+ 64 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configurers/X509ConfigurerTests.java

@@ -38,6 +38,7 @@ import java.security.cert.X509Certificate;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.springframework.security.config.Customizer.withDefaults;
 import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.x509;
 import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@@ -122,6 +123,69 @@ public class X509ConfigurerTests {
 		}
 	}
 
+	@Test
+	public void x509WhenConfiguredInLambdaThenUsesDefaults() throws Exception {
+		this.spring.register(DefaultsInLambdaConfig.class).autowire();
+		X509Certificate certificate = loadCert("rod.cer");
+
+		this.mvc.perform(get("/")
+				.with(x509(certificate)))
+				.andExpect(authenticated().withUsername("rod"));
+	}
+
+	@EnableWebSecurity
+	static class DefaultsInLambdaConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.x509(withDefaults());
+			// @formatter:on
+		}
+
+		@Override
+		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+			// @formatter:off
+			auth
+				.inMemoryAuthentication()
+					.withUser("rod").password("password").roles("USER", "ADMIN");
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void x509WhenSubjectPrincipalRegexInLambdaThenUsesRegexToExtractPrincipal() throws Exception {
+		this.spring.register(SubjectPrincipalRegexInLambdaConfig.class).autowire();
+		X509Certificate certificate = loadCert("rodatexampledotcom.cer");
+
+		this.mvc.perform(get("/")
+				.with(x509(certificate)))
+				.andExpect(authenticated().withUsername("rod"));
+	}
+
+	@EnableWebSecurity
+	static class SubjectPrincipalRegexInLambdaConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.x509(x509 ->
+					x509
+						.subjectPrincipalRegex("CN=(.*?)@example.com(?:,|$)")
+				);
+			// @formatter:on
+		}
+
+		@Override
+		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+			// @formatter:off
+			auth
+				.inMemoryAuthentication()
+					.withUser("rod").password("password").roles("USER", "ADMIN");
+			// @formatter:on
+		}
+	}
+
 	private <T extends Certificate> T loadCert(String location) {
 		try (InputStream is = new ClassPathResource(location).getInputStream()) {
 			CertificateFactory certFactory = CertificateFactory.getInstance("X.509");