|
16 | 16 |
|
17 | 17 | package org.springframework.security.config.annotation.web.configurers;
|
18 | 18 |
|
| 19 | +import java.io.IOException; |
| 20 | + |
| 21 | +import jakarta.servlet.DispatcherType; |
| 22 | +import jakarta.servlet.Filter; |
| 23 | +import jakarta.servlet.FilterChain; |
| 24 | +import jakarta.servlet.ServletException; |
| 25 | +import jakarta.servlet.ServletRequest; |
| 26 | +import jakarta.servlet.ServletResponse; |
19 | 27 | import jakarta.servlet.http.HttpServletRequest;
|
20 | 28 | import jakarta.servlet.http.HttpServletResponse;
|
21 | 29 | import jakarta.servlet.http.HttpSession;
|
|
25 | 33 | import org.springframework.beans.factory.annotation.Autowired;
|
26 | 34 | import org.springframework.context.annotation.Bean;
|
27 | 35 | import org.springframework.context.annotation.Configuration;
|
| 36 | +import org.springframework.http.HttpStatus; |
| 37 | +import org.springframework.http.ResponseEntity; |
| 38 | +import org.springframework.mock.web.MockFilterChain; |
| 39 | +import org.springframework.mock.web.MockHttpServletRequest; |
28 | 40 | import org.springframework.mock.web.MockHttpSession;
|
29 | 41 | import org.springframework.security.authentication.AuthenticationTrustResolver;
|
| 42 | +import org.springframework.security.config.Customizer; |
30 | 43 | import org.springframework.security.config.TestDeferredSecurityContext;
|
31 | 44 | import org.springframework.security.config.annotation.ObjectPostProcessor;
|
32 | 45 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|
55 | 68 | import org.springframework.test.web.servlet.MvcResult;
|
56 | 69 | import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
57 | 70 | import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
| 71 | +import org.springframework.web.bind.annotation.GetMapping; |
58 | 72 | import org.springframework.web.bind.annotation.RequestMapping;
|
59 | 73 | import org.springframework.web.bind.annotation.RestController;
|
| 74 | +import org.springframework.web.util.WebUtils; |
60 | 75 |
|
61 | 76 | import static org.assertj.core.api.Assertions.assertThat;
|
62 | 77 | import static org.mockito.ArgumentMatchers.any;
|
@@ -360,6 +375,84 @@ public void loginWhenSessionCreationPolicyStatelessThenSecurityContextIsAvailabl
|
360 | 375 | assertThat(securityContext).isNotNull();
|
361 | 376 | }
|
362 | 377 |
|
| 378 | + /** |
| 379 | + * This ensures that if an ErrorDispatch occurs, then the SecurityContextRepository |
| 380 | + * defaulted by SessionManagementConfigurer is correct (looks at both Session and |
| 381 | + * Request Attributes). |
| 382 | + * @throws Exception |
| 383 | + */ |
| 384 | + @Test |
| 385 | + public void gh12070WhenErrorDispatchSecurityContextRepositoryWorks() throws Exception { |
| 386 | + Filter errorDispatchFilter = new Filter() { |
| 387 | + @Override |
| 388 | + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) |
| 389 | + throws IOException, ServletException { |
| 390 | + try { |
| 391 | + chain.doFilter(request, response); |
| 392 | + } |
| 393 | + catch (ServletException ex) { |
| 394 | + if (request.getDispatcherType() == DispatcherType.ERROR) { |
| 395 | + throw ex; |
| 396 | + } |
| 397 | + MockHttpServletRequest httpRequest = WebUtils.getNativeRequest(request, |
| 398 | + MockHttpServletRequest.class); |
| 399 | + httpRequest.setDispatcherType(DispatcherType.ERROR); |
| 400 | + // necessary to prevent HttpBasicFilter from invoking again |
| 401 | + httpRequest.setAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE, "/error"); |
| 402 | + httpRequest.setRequestURI("/error"); |
| 403 | + MockFilterChain mockChain = (MockFilterChain) chain; |
| 404 | + mockChain.reset(); |
| 405 | + mockChain.doFilter(httpRequest, response); |
| 406 | + } |
| 407 | + } |
| 408 | + }; |
| 409 | + this.spring.addFilter(errorDispatchFilter).register(Gh12070IssueConfig.class).autowire(); |
| 410 | + |
| 411 | + // @formatter:off |
| 412 | + this.mvc.perform(get("/500").with(httpBasic("user", "password"))) |
| 413 | + .andExpect(status().isInternalServerError()); |
| 414 | + // @formatter:on |
| 415 | + } |
| 416 | + |
| 417 | + @Configuration |
| 418 | + @EnableWebSecurity |
| 419 | + static class Gh12070IssueConfig { |
| 420 | + |
| 421 | + @Bean |
| 422 | + SecurityFilterChain filterChain(HttpSecurity http) throws Exception { |
| 423 | + // @formatter:off |
| 424 | + http |
| 425 | + .authorizeHttpRequests((authorize) -> authorize |
| 426 | + .anyRequest().authenticated() |
| 427 | + ) |
| 428 | + .httpBasic(Customizer.withDefaults()) |
| 429 | + .formLogin(Customizer.withDefaults()); |
| 430 | + return http.build(); |
| 431 | + // @formatter:on |
| 432 | + } |
| 433 | + |
| 434 | + @Bean |
| 435 | + UserDetailsService userDetailsService() { |
| 436 | + return new InMemoryUserDetailsManager(PasswordEncodedUser.user()); |
| 437 | + } |
| 438 | + |
| 439 | + @RestController |
| 440 | + static class ErrorController { |
| 441 | + |
| 442 | + @GetMapping("/500") |
| 443 | + String error() throws ServletException { |
| 444 | + throw new ServletException("Error"); |
| 445 | + } |
| 446 | + |
| 447 | + @GetMapping("/error") |
| 448 | + ResponseEntity<String> errorHandler() { |
| 449 | + return new ResponseEntity<>("error", HttpStatus.INTERNAL_SERVER_ERROR); |
| 450 | + } |
| 451 | + |
| 452 | + } |
| 453 | + |
| 454 | + } |
| 455 | + |
363 | 456 | @Configuration
|
364 | 457 | @EnableWebSecurity
|
365 | 458 | static class SessionManagementRequestCacheConfig {
|
|
0 commit comments