Consistently set AuthenticationEventPublisher in AuthenticationManagerBuilder
Prior to this, the HttpSecurity bean was not consistent with WebSecurityConfigurerAdapter's HttpSecurity because it did not setup a default AuthenticationEventPublisher. This also fixes a problem where the AuthenticationEventPublisher bean would only be considered if there was a UserDetailsService Closes gh-11449 Closes gh-11726
This commit is contained in:
parent
888c65a936
commit
0aac515737
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -39,6 +39,7 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
|||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
|
||||
|
@ -79,8 +80,7 @@ public class AuthenticationConfiguration {
|
|||
public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,
|
||||
ApplicationContext context) {
|
||||
LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
|
||||
AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context,
|
||||
AuthenticationEventPublisher.class);
|
||||
AuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context);
|
||||
DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(
|
||||
objectPostProcessor, defaultPasswordEncoder);
|
||||
if (authenticationEventPublisher != null) {
|
||||
|
@ -142,6 +142,13 @@ public class AuthenticationConfiguration {
|
|||
this.objectPostProcessor = objectPostProcessor;
|
||||
}
|
||||
|
||||
private AuthenticationEventPublisher getAuthenticationEventPublisher(ApplicationContext context) {
|
||||
if (context.getBeanNamesForType(AuthenticationEventPublisher.class).length > 0) {
|
||||
return context.getBean(AuthenticationEventPublisher.class);
|
||||
}
|
||||
return this.objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T lazyBean(Class<T> interfaceName) {
|
||||
LazyInitTargetSource lazyTargetSource = new LazyInitTargetSource();
|
||||
|
|
|
@ -26,7 +26,9 @@ import org.springframework.context.annotation.Bean;
|
|||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
|
@ -95,6 +97,7 @@ class HttpSecurityConfiguration {
|
|||
AuthenticationManagerBuilder authenticationBuilder = new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(
|
||||
this.objectPostProcessor, passwordEncoder);
|
||||
authenticationBuilder.parentAuthenticationManager(authenticationManager());
|
||||
authenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher());
|
||||
HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
|
||||
WebAsyncManagerIntegrationFilter webAsyncManagerIntegrationFilter = new WebAsyncManagerIntegrationFilter();
|
||||
webAsyncManagerIntegrationFilter.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||
|
@ -121,6 +124,13 @@ class HttpSecurityConfiguration {
|
|||
: this.authenticationConfiguration.getAuthenticationManager();
|
||||
}
|
||||
|
||||
private AuthenticationEventPublisher getAuthenticationEventPublisher() {
|
||||
if (this.context.getBeanNamesForType(AuthenticationEventPublisher.class).length > 0) {
|
||||
return this.context.getBean(AuthenticationEventPublisher.class);
|
||||
}
|
||||
return this.objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
|
||||
}
|
||||
|
||||
private void applyDefaultConfigurers(HttpSecurity http) throws Exception {
|
||||
ClassLoader classLoader = this.context.getClassLoader();
|
||||
List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader
|
||||
|
|
|
@ -34,8 +34,10 @@ import org.springframework.core.Ordered;
|
|||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.TestAuthentication;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
|
@ -51,6 +53,7 @@ import org.springframework.security.config.annotation.web.servlet.configuration.
|
|||
import org.springframework.security.config.test.SpringTestContext;
|
||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||
import org.springframework.security.config.users.AuthenticationTestConfiguration;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
@ -62,6 +65,7 @@ import org.springframework.security.core.userdetails.UserDetailsService;
|
|||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
@ -296,6 +300,28 @@ public class AuthenticationConfigurationTests {
|
|||
assertThatExceptionOfType(AlreadyBuiltException.class).isThrownBy(ap::build);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenDefaultsThenDefaultAuthenticationEventPublisher() {
|
||||
this.spring.register(AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class).autowire();
|
||||
AuthenticationManagerBuilder authenticationManagerBuilder = this.spring.getContext()
|
||||
.getBean(AuthenticationManagerBuilder.class);
|
||||
AuthenticationEventPublisher eventPublisher = (AuthenticationEventPublisher) ReflectionTestUtils
|
||||
.getField(authenticationManagerBuilder, "eventPublisher");
|
||||
assertThat(eventPublisher).isInstanceOf(DefaultAuthenticationEventPublisher.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenCustomAuthenticationEventPublisherThenCustomAuthenticationEventPublisher() {
|
||||
this.spring.register(AuthenticationConfiguration.class, ObjectPostProcessorConfiguration.class,
|
||||
CustomAuthenticationEventPublisherConfig.class).autowire();
|
||||
AuthenticationManagerBuilder authenticationManagerBuilder = this.spring.getContext()
|
||||
.getBean(AuthenticationManagerBuilder.class);
|
||||
AuthenticationEventPublisher eventPublisher = (AuthenticationEventPublisher) ReflectionTestUtils
|
||||
.getField(authenticationManagerBuilder, "eventPublisher");
|
||||
assertThat(eventPublisher)
|
||||
.isInstanceOf(CustomAuthenticationEventPublisherConfig.MyAuthenticationEventPublisher.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true)
|
||||
static class GlobalMethodSecurityAutowiredConfig {
|
||||
|
@ -351,6 +377,30 @@ public class AuthenticationConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomAuthenticationEventPublisherConfig {
|
||||
|
||||
@Bean
|
||||
AuthenticationEventPublisher eventPublisher() {
|
||||
return new MyAuthenticationEventPublisher();
|
||||
}
|
||||
|
||||
static class MyAuthenticationEventPublisher implements AuthenticationEventPublisher {
|
||||
|
||||
@Override
|
||||
public void publishAuthenticationSuccess(Authentication authentication) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface Service {
|
||||
|
||||
void run();
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
|
||||
package org.springframework.security.config.annotation.web.configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import com.google.common.net.HttpHeaders;
|
||||
|
@ -31,16 +33,22 @@ import org.springframework.beans.factory.BeanCreationException;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authentication.event.AbstractAuthenticationEvent;
|
||||
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
|
||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
||||
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.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
@ -59,6 +67,7 @@ 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.SecurityMockMvcRequestBuilders.formLogin;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||
|
@ -230,6 +239,48 @@ public class HttpSecurityConfigurationTests {
|
|||
this.mockMvc.perform(get("/login?logout")).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenUsingDefaultThenAuthenticationEventPublished() throws Exception {
|
||||
this.spring
|
||||
.register(SecurityEnabledConfig.class, UserDetailsConfig.class, AuthenticationEventListenerConfig.class)
|
||||
.autowire();
|
||||
AuthenticationEventListenerConfig.clearEvents();
|
||||
this.mockMvc.perform(formLogin()).andExpect(status().is3xxRedirection());
|
||||
assertThat(AuthenticationEventListenerConfig.EVENTS).isNotEmpty();
|
||||
assertThat(AuthenticationEventListenerConfig.EVENTS).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenUsingDefaultAndNoUserDetailsServiceThenAuthenticationEventPublished() throws Exception {
|
||||
this.spring
|
||||
.register(SecurityEnabledConfig.class, UserDetailsConfig.class, AuthenticationEventListenerConfig.class)
|
||||
.autowire();
|
||||
AuthenticationEventListenerConfig.clearEvents();
|
||||
this.mockMvc.perform(formLogin()).andExpect(status().is3xxRedirection());
|
||||
assertThat(AuthenticationEventListenerConfig.EVENTS).isNotEmpty();
|
||||
assertThat(AuthenticationEventListenerConfig.EVENTS).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenUsingCustomAuthenticationEventPublisherThenAuthenticationEventPublished() throws Exception {
|
||||
this.spring.register(SecurityEnabledConfig.class, UserDetailsConfig.class,
|
||||
CustomAuthenticationEventPublisherConfig.class).autowire();
|
||||
CustomAuthenticationEventPublisherConfig.clearEvents();
|
||||
this.mockMvc.perform(formLogin()).andExpect(status().is3xxRedirection());
|
||||
assertThat(CustomAuthenticationEventPublisherConfig.EVENTS).isNotEmpty();
|
||||
assertThat(CustomAuthenticationEventPublisherConfig.EVENTS).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWhenUsingCustomAuthenticationEventPublisherAndNoUserDetailsServiceThenAuthenticationEventPublished()
|
||||
throws Exception {
|
||||
this.spring.register(SecurityEnabledConfig.class, CustomAuthenticationEventPublisherConfig.class).autowire();
|
||||
CustomAuthenticationEventPublisherConfig.clearEvents();
|
||||
this.mockMvc.perform(formLogin()).andExpect(status().is3xxRedirection());
|
||||
assertThat(CustomAuthenticationEventPublisherConfig.EVENTS).isNotEmpty();
|
||||
assertThat(CustomAuthenticationEventPublisherConfig.EVENTS).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenAuthorizeHttpRequestsBeforeAuthorizeRequestThenException() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
|
@ -372,6 +423,55 @@ public class HttpSecurityConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CustomAuthenticationEventPublisherConfig {
|
||||
|
||||
static List<Authentication> EVENTS = new ArrayList<>();
|
||||
|
||||
static void clearEvents() {
|
||||
EVENTS.clear();
|
||||
}
|
||||
|
||||
@Bean
|
||||
AuthenticationEventPublisher publisher() {
|
||||
return new AuthenticationEventPublisher() {
|
||||
|
||||
@Override
|
||||
public void publishAuthenticationSuccess(Authentication authentication) {
|
||||
EVENTS.add(authentication);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishAuthenticationFailure(AuthenticationException exception,
|
||||
Authentication authentication) {
|
||||
EVENTS.add(authentication);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AuthenticationEventListenerConfig {
|
||||
|
||||
static List<AbstractAuthenticationEvent> EVENTS = new ArrayList<>();
|
||||
|
||||
static void clearEvents() {
|
||||
EVENTS.clear();
|
||||
}
|
||||
|
||||
@EventListener
|
||||
void onAuthenticationSuccessEvent(AuthenticationSuccessEvent event) {
|
||||
EVENTS.add(event);
|
||||
}
|
||||
|
||||
@EventListener
|
||||
void onAuthenticationFailureEvent(AbstractAuthenticationFailureEvent event) {
|
||||
EVENTS.add(event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class BaseController {
|
||||
|
||||
|
|
Loading…
Reference in New Issue