Use SecurityContextHolderStrategy for Async Requests

Issue gh-11060
Issue gh-11061
This commit is contained in:
Josh Cummings
2022-06-21 16:45:10 -06:00
parent 135e602472
commit 27de315e5e
7 changed files with 152 additions and 11 deletions

View File

@@ -36,11 +36,13 @@ import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -55,6 +57,8 @@ import org.springframework.web.bind.annotation.RestController;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
@@ -135,6 +139,22 @@ public class HttpSecurityConfigurationTests {
// @formatter:on
}
@Test
public void asyncDispatchWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {
this.spring.register(DefaultWithFilterChainConfig.class, SecurityContextChangedListenerConfig.class,
NameController.class).autowire();
// @formatter:off
MockHttpServletRequestBuilder requestWithBob = get("/name").with(user("Bob"));
MvcResult mvcResult = this.mockMvc.perform(requestWithBob)
.andExpect(request().asyncStarted())
.andReturn();
this.mockMvc.perform(asyncDispatch(mvcResult))
.andExpect(status().isOk())
.andExpect(content().string("Bob"));
// @formatter:on
verify(this.spring.getContext().getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();
}
@Test
public void getWhenDefaultFilterChainBeanThenAnonymousPermitted() throws Exception {
this.spring.register(AuthorizeRequestsConfig.class, UserDetailsConfig.class, BaseController.class).autowire();
@@ -244,8 +264,8 @@ public class HttpSecurityConfigurationTests {
static class NameController {
@GetMapping("/name")
Callable<String> name() {
return () -> SecurityContextHolder.getContext().getAuthentication().getName();
Callable<String> name(Authentication authentication) {
return () -> authentication.getName();
}
}

View File

@@ -27,6 +27,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import javax.security.auth.Subject;
@@ -129,12 +130,15 @@ import static org.mockito.Mockito.verify;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.x509;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
@@ -766,6 +770,21 @@ public class MiscHttpConfigTests {
// @formatter:on
}
@Test
public void asyncDispatchWhenCustomSecurityContextHolderStrategyThenUses() throws Exception {
this.spring.configLocations(xml("WithSecurityContextHolderStrategy")).autowire();
// @formatter:off
MockHttpServletRequestBuilder requestWithBob = get("/name").with(user("Bob"));
MvcResult mvcResult = this.mvc.perform(requestWithBob)
.andExpect(request().asyncStarted())
.andReturn();
this.mvc.perform(asyncDispatch(mvcResult))
.andExpect(status().isOk())
.andExpect(content().string("Bob"));
// @formatter:on
verify(this.spring.getContext().getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext();
}
/**
* SEC-1893
*/
@@ -909,6 +928,11 @@ public class MiscHttpConfigTests {
return authentication.getDetails().getClass().getName();
}
@GetMapping("/name")
Callable<String> name(Authentication authentication) {
return () -> authentication.getName();
}
}
@RestController