|
16 | 16 |
|
17 | 17 | package org.springframework.security.config.annotation.web.configuration;
|
18 | 18 |
|
| 19 | +import java.util.ArrayList; |
19 | 20 | import java.util.Arrays;
|
| 21 | +import java.util.List; |
20 | 22 | import java.util.concurrent.Callable;
|
21 | 23 |
|
22 | 24 | import javax.servlet.http.HttpServletRequest;
|
|
32 | 34 | import org.springframework.beans.factory.annotation.Autowired;
|
33 | 35 | import org.springframework.context.annotation.Bean;
|
34 | 36 | import org.springframework.context.annotation.Configuration;
|
| 37 | +import org.springframework.context.event.EventListener; |
35 | 38 | import org.springframework.core.io.support.SpringFactoriesLoader;
|
36 | 39 | import org.springframework.mock.web.MockHttpSession;
|
37 | 40 | import org.springframework.security.access.AccessDeniedException;
|
| 41 | +import org.springframework.security.authentication.AuthenticationEventPublisher; |
38 | 42 | import org.springframework.security.authentication.TestingAuthenticationToken;
|
| 43 | +import org.springframework.security.authentication.event.AbstractAuthenticationEvent; |
| 44 | +import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent; |
| 45 | +import org.springframework.security.authentication.event.AuthenticationSuccessEvent; |
39 | 46 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
40 | 47 | import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
41 | 48 | import org.springframework.security.config.test.SpringTestContext;
|
42 | 49 | import org.springframework.security.config.test.SpringTestContextExtension;
|
| 50 | +import org.springframework.security.core.Authentication; |
| 51 | +import org.springframework.security.core.AuthenticationException; |
43 | 52 | import org.springframework.security.core.context.SecurityContextHolder;
|
44 | 53 | import org.springframework.security.core.userdetails.User;
|
45 | 54 | import org.springframework.security.core.userdetails.UserDetails;
|
|
56 | 65 | import static org.assertj.core.api.Assertions.assertThat;
|
57 | 66 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
58 | 67 | import static org.springframework.security.config.Customizer.withDefaults;
|
| 68 | +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin; |
59 | 69 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
60 | 70 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
61 | 71 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
@@ -211,6 +221,48 @@ public void loginWhenUsingDefaultsThenDefaultLogoutSuccessPageGenerated() throws
|
211 | 221 | this.mockMvc.perform(get("/login?logout")).andExpect(status().isOk());
|
212 | 222 | }
|
213 | 223 |
|
| 224 | + @Test |
| 225 | + public void loginWhenUsingDefaultThenAuthenticationEventPublished() throws Exception { |
| 226 | + this.spring |
| 227 | + .register(SecurityEnabledConfig.class, UserDetailsConfig.class, AuthenticationEventListenerConfig.class) |
| 228 | + .autowire(); |
| 229 | + AuthenticationEventListenerConfig.clearEvents(); |
| 230 | + this.mockMvc.perform(formLogin()).andExpect(status().is3xxRedirection()); |
| 231 | + assertThat(AuthenticationEventListenerConfig.EVENTS).isNotEmpty(); |
| 232 | + assertThat(AuthenticationEventListenerConfig.EVENTS).hasSize(1); |
| 233 | + } |
| 234 | + |
| 235 | + @Test |
| 236 | + public void loginWhenUsingDefaultAndNoUserDetailsServiceThenAuthenticationEventPublished() throws Exception { |
| 237 | + this.spring |
| 238 | + .register(SecurityEnabledConfig.class, UserDetailsConfig.class, AuthenticationEventListenerConfig.class) |
| 239 | + .autowire(); |
| 240 | + AuthenticationEventListenerConfig.clearEvents(); |
| 241 | + this.mockMvc.perform(formLogin()).andExpect(status().is3xxRedirection()); |
| 242 | + assertThat(AuthenticationEventListenerConfig.EVENTS).isNotEmpty(); |
| 243 | + assertThat(AuthenticationEventListenerConfig.EVENTS).hasSize(1); |
| 244 | + } |
| 245 | + |
| 246 | + @Test |
| 247 | + public void loginWhenUsingCustomAuthenticationEventPublisherThenAuthenticationEventPublished() throws Exception { |
| 248 | + this.spring.register(SecurityEnabledConfig.class, UserDetailsConfig.class, |
| 249 | + CustomAuthenticationEventPublisherConfig.class).autowire(); |
| 250 | + CustomAuthenticationEventPublisherConfig.clearEvents(); |
| 251 | + this.mockMvc.perform(formLogin()).andExpect(status().is3xxRedirection()); |
| 252 | + assertThat(CustomAuthenticationEventPublisherConfig.EVENTS).isNotEmpty(); |
| 253 | + assertThat(CustomAuthenticationEventPublisherConfig.EVENTS).hasSize(1); |
| 254 | + } |
| 255 | + |
| 256 | + @Test |
| 257 | + public void loginWhenUsingCustomAuthenticationEventPublisherAndNoUserDetailsServiceThenAuthenticationEventPublished() |
| 258 | + throws Exception { |
| 259 | + this.spring.register(SecurityEnabledConfig.class, CustomAuthenticationEventPublisherConfig.class).autowire(); |
| 260 | + CustomAuthenticationEventPublisherConfig.clearEvents(); |
| 261 | + this.mockMvc.perform(formLogin()).andExpect(status().is3xxRedirection()); |
| 262 | + assertThat(CustomAuthenticationEventPublisherConfig.EVENTS).isNotEmpty(); |
| 263 | + assertThat(CustomAuthenticationEventPublisherConfig.EVENTS).hasSize(1); |
| 264 | + } |
| 265 | + |
214 | 266 | @Test
|
215 | 267 | public void configureWhenAuthorizeHttpRequestsBeforeAuthorizeRequestThenException() {
|
216 | 268 | assertThatExceptionOfType(BeanCreationException.class)
|
@@ -348,6 +400,55 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
348 | 400 |
|
349 | 401 | }
|
350 | 402 |
|
| 403 | + @Configuration |
| 404 | + static class CustomAuthenticationEventPublisherConfig { |
| 405 | + |
| 406 | + static List<Authentication> EVENTS = new ArrayList<>(); |
| 407 | + |
| 408 | + static void clearEvents() { |
| 409 | + EVENTS.clear(); |
| 410 | + } |
| 411 | + |
| 412 | + @Bean |
| 413 | + AuthenticationEventPublisher publisher() { |
| 414 | + return new AuthenticationEventPublisher() { |
| 415 | + |
| 416 | + @Override |
| 417 | + public void publishAuthenticationSuccess(Authentication authentication) { |
| 418 | + EVENTS.add(authentication); |
| 419 | + } |
| 420 | + |
| 421 | + @Override |
| 422 | + public void publishAuthenticationFailure(AuthenticationException exception, |
| 423 | + Authentication authentication) { |
| 424 | + EVENTS.add(authentication); |
| 425 | + } |
| 426 | + }; |
| 427 | + } |
| 428 | + |
| 429 | + } |
| 430 | + |
| 431 | + @Configuration |
| 432 | + static class AuthenticationEventListenerConfig { |
| 433 | + |
| 434 | + static List<AbstractAuthenticationEvent> EVENTS = new ArrayList<>(); |
| 435 | + |
| 436 | + static void clearEvents() { |
| 437 | + EVENTS.clear(); |
| 438 | + } |
| 439 | + |
| 440 | + @EventListener |
| 441 | + void onAuthenticationSuccessEvent(AuthenticationSuccessEvent event) { |
| 442 | + EVENTS.add(event); |
| 443 | + } |
| 444 | + |
| 445 | + @EventListener |
| 446 | + void onAuthenticationFailureEvent(AbstractAuthenticationFailureEvent event) { |
| 447 | + EVENTS.add(event); |
| 448 | + } |
| 449 | + |
| 450 | + } |
| 451 | + |
351 | 452 | @RestController
|
352 | 453 | static class BaseController {
|
353 | 454 |
|
|
0 commit comments