|
1 | 1 | /*
|
2 |
| - * Copyright 2002-2019 the original author or authors. |
| 2 | + * Copyright 2002-2023 the original author or authors. |
3 | 3 | *
|
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | 5 | * you may not use this file except in compliance with the License.
|
|
16 | 16 |
|
17 | 17 | package org.springframework.security.config.annotation.web.configurers;
|
18 | 18 |
|
| 19 | +import javax.servlet.http.HttpServletRequest; |
| 20 | +import javax.servlet.http.HttpServletResponse; |
| 21 | + |
19 | 22 | import org.apache.http.HttpHeaders;
|
20 | 23 | import org.junit.jupiter.api.Test;
|
21 | 24 | import org.junit.jupiter.api.extension.ExtendWith;
|
22 | 25 |
|
23 | 26 | import org.springframework.beans.factory.BeanCreationException;
|
24 | 27 | import org.springframework.beans.factory.annotation.Autowired;
|
25 | 28 | import org.springframework.context.annotation.Bean;
|
| 29 | +import org.springframework.context.annotation.Configuration; |
26 | 30 | import org.springframework.http.MediaType;
|
| 31 | +import org.springframework.mock.web.MockHttpSession; |
| 32 | +import org.springframework.security.config.Customizer; |
27 | 33 | import org.springframework.security.config.annotation.ObjectPostProcessor;
|
28 | 34 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
29 | 35 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
30 | 36 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
31 | 37 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
32 | 38 | import org.springframework.security.config.test.SpringTestContext;
|
33 | 39 | import org.springframework.security.config.test.SpringTestContextExtension;
|
| 40 | +import org.springframework.security.web.SecurityFilterChain; |
34 | 41 | import org.springframework.security.web.authentication.RememberMeServices;
|
35 | 42 | import org.springframework.security.web.authentication.logout.LogoutFilter;
|
36 | 43 | import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
| 44 | +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; |
| 45 | +import org.springframework.security.web.context.SecurityContextRepository; |
37 | 46 | import org.springframework.security.web.util.matcher.RequestMatcher;
|
38 | 47 | import org.springframework.test.web.servlet.MockMvc;
|
39 | 48 | import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
|
42 | 51 | import static org.mockito.ArgumentMatchers.any;
|
43 | 52 | import static org.mockito.Mockito.mock;
|
44 | 53 | import static org.mockito.Mockito.spy;
|
| 54 | +import static org.mockito.Mockito.times; |
45 | 55 | import static org.mockito.Mockito.verify;
|
46 | 56 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
47 | 57 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
@@ -302,6 +312,80 @@ public void logoutWhenDisabledThenLogoutUrlNotFound() throws Exception {
|
302 | 312 | this.mvc.perform(post("/logout").with(csrf())).andExpect(status().isNotFound());
|
303 | 313 | }
|
304 | 314 |
|
| 315 | + @Test |
| 316 | + public void logoutWhenCustomSecurityContextRepositoryThenUses() throws Exception { |
| 317 | + CustomSecurityContextRepositoryConfig.repository = mock(SecurityContextRepository.class); |
| 318 | + this.spring.register(CustomSecurityContextRepositoryConfig.class).autowire(); |
| 319 | + // @formatter:off |
| 320 | + MockHttpServletRequestBuilder logoutRequest = post("/logout") |
| 321 | + .with(csrf()) |
| 322 | + .with(user("user")) |
| 323 | + .header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML_VALUE); |
| 324 | + this.mvc.perform(logoutRequest) |
| 325 | + .andExpect(status().isFound()) |
| 326 | + .andExpect(redirectedUrl("/login?logout")); |
| 327 | + // @formatter:on |
| 328 | + int invocationCount = 2; // 1 from user() post processor and 1 from |
| 329 | + // SecurityContextLogoutHandler |
| 330 | + verify(CustomSecurityContextRepositoryConfig.repository, times(invocationCount)).saveContext(any(), |
| 331 | + any(HttpServletRequest.class), any(HttpServletResponse.class)); |
| 332 | + } |
| 333 | + |
| 334 | + @Test |
| 335 | + public void logoutWhenNoSecurityContextRepositoryThenHttpSessionSecurityContextRepository() throws Exception { |
| 336 | + this.spring.register(InvalidateHttpSessionFalseConfig.class).autowire(); |
| 337 | + MockHttpSession session = mock(MockHttpSession.class); |
| 338 | + // @formatter:off |
| 339 | + MockHttpServletRequestBuilder logoutRequest = post("/logout") |
| 340 | + .with(csrf()) |
| 341 | + .with(user("user")) |
| 342 | + .session(session) |
| 343 | + .header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML_VALUE); |
| 344 | + this.mvc.perform(logoutRequest) |
| 345 | + .andExpect(status().isFound()) |
| 346 | + .andExpect(redirectedUrl("/login?logout")) |
| 347 | + .andReturn(); |
| 348 | + // @formatter:on |
| 349 | + verify(session).removeAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); |
| 350 | + } |
| 351 | + |
| 352 | + @Configuration |
| 353 | + @EnableWebSecurity |
| 354 | + static class InvalidateHttpSessionFalseConfig { |
| 355 | + |
| 356 | + @Bean |
| 357 | + SecurityFilterChain filterChain(HttpSecurity http) throws Exception { |
| 358 | + // @formatter:off |
| 359 | + http |
| 360 | + .logout((logout) -> logout.invalidateHttpSession(false)) |
| 361 | + .securityContext((context) -> context.requireExplicitSave(true)); |
| 362 | + return http.build(); |
| 363 | + // @formatter:on |
| 364 | + } |
| 365 | + |
| 366 | + } |
| 367 | + |
| 368 | + @Configuration |
| 369 | + @EnableWebSecurity |
| 370 | + static class CustomSecurityContextRepositoryConfig { |
| 371 | + |
| 372 | + static SecurityContextRepository repository; |
| 373 | + |
| 374 | + @Bean |
| 375 | + SecurityFilterChain filterChain(HttpSecurity http) throws Exception { |
| 376 | + // @formatter:off |
| 377 | + http |
| 378 | + .logout(Customizer.withDefaults()) |
| 379 | + .securityContext((context) -> context |
| 380 | + .requireExplicitSave(true) |
| 381 | + .securityContextRepository(repository) |
| 382 | + ); |
| 383 | + return http.build(); |
| 384 | + // @formatter:on |
| 385 | + } |
| 386 | + |
| 387 | + } |
| 388 | + |
305 | 389 | @EnableWebSecurity
|
306 | 390 | static class NullLogoutSuccessHandlerConfig extends WebSecurityConfigurerAdapter {
|
307 | 391 |
|
|
0 commit comments