|
@@ -1,5 +1,5 @@
|
|
/*
|
|
/*
|
|
- * Copyright 2002-2022 the original author or authors.
|
|
|
|
|
|
+ * Copyright 2002-2023 the original author or authors.
|
|
*
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* you may not use this file except in compliance with the License.
|
|
@@ -32,6 +32,7 @@ import org.springframework.security.config.annotation.ObjectPostProcessor;
|
|
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
|
|
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
|
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
|
|
|
+import org.springframework.security.config.http.SessionCreationPolicy;
|
|
import org.springframework.security.config.test.SpringTestContext;
|
|
import org.springframework.security.config.test.SpringTestContext;
|
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
|
import org.springframework.security.core.context.SecurityContextChangedListener;
|
|
import org.springframework.security.core.context.SecurityContextChangedListener;
|
|
@@ -45,6 +46,7 @@ import org.springframework.security.web.authentication.preauth.PreAuthenticatedA
|
|
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
|
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
|
import org.springframework.test.web.servlet.MockMvc;
|
|
import org.springframework.test.web.servlet.MockMvc;
|
|
|
|
|
|
|
|
+import static org.assertj.core.api.Assertions.assertThat;
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
import static org.mockito.Mockito.atLeastOnce;
|
|
import static org.mockito.Mockito.atLeastOnce;
|
|
import static org.mockito.Mockito.mock;
|
|
import static org.mockito.Mockito.mock;
|
|
@@ -141,6 +143,18 @@ public class X509ConfigurerTests {
|
|
// @formatter:on
|
|
// @formatter:on
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // gh-13008
|
|
|
|
+ @Test
|
|
|
|
+ public void x509WhenStatelessSessionManagementThenDoesNotCreateSession() throws Exception {
|
|
|
|
+ this.spring.register(StatelessSessionManagementConfig.class).autowire();
|
|
|
|
+ X509Certificate certificate = loadCert("rodatexampledotcom.cer");
|
|
|
|
+ // @formatter:off
|
|
|
|
+ this.mvc.perform(get("/").with(x509(certificate)))
|
|
|
|
+ .andExpect((result) -> assertThat(result.getRequest().getSession(false)).isNull())
|
|
|
|
+ .andExpect(authenticated().withUsername("rod"));
|
|
|
|
+ // @formatter:on
|
|
|
|
+ }
|
|
|
|
+
|
|
private <T extends Certificate> T loadCert(String location) {
|
|
private <T extends Certificate> T loadCert(String location) {
|
|
try (InputStream is = new ClassPathResource(location).getInputStream()) {
|
|
try (InputStream is = new ClassPathResource(location).getInputStream()) {
|
|
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
|
|
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
|
|
@@ -311,4 +325,27 @@ public class X509ConfigurerTests {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ @Configuration
|
|
|
|
+ @EnableWebSecurity
|
|
|
|
+ static class StatelessSessionManagementConfig {
|
|
|
|
+
|
|
|
|
+ @Bean
|
|
|
|
+ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
|
|
|
+ // @formatter:off
|
|
|
|
+ http
|
|
|
|
+ .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
|
|
|
+ .x509((x509) -> x509.subjectPrincipalRegex("CN=(.*?)@example.com(?:,|$)"));
|
|
|
|
+ // @formatter:on
|
|
|
|
+ return http.build();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Bean
|
|
|
|
+ UserDetailsService userDetailsService() {
|
|
|
|
+ UserDetails user = User.withDefaultPasswordEncoder().username("rod").password("password")
|
|
|
|
+ .roles("USER", "ADMIN").build();
|
|
|
|
+ return new InMemoryUserDetailsManager(user);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
}
|
|
}
|