'@EventListener for AuthenticationSuccessEvent or InteractiveAuthenticationSuccessEvent not fired

I have this listener in the context of Spring:

package listeners;

import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.security.authentication.event.AbstractAuthenticationEvent;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.stereotype.Component;
import services.UserService;
import services.security.CustomUserDetails;

/**
 *
 * @author sergio
 */
@Component
public class AuthenticationSuccessEventHandler{

    private static Logger logger = LoggerFactory.getLogger(AuthenticationSuccessEventHandler.class);

    @Autowired
    private UserService userService;

    @EventListener({AuthenticationSuccessEvent.class, InteractiveAuthenticationSuccessEvent.class})
    public void processAuthenticationSuccessEvent(AbstractAuthenticationEvent  e) {
        logger.info("Autenticación realizada ....");
        // Actualizamos la útltima fecha de acceso
        String username = ((CustomUserDetails) e.getAuthentication().getPrincipal()).getUsername();
        logger.info("Actualizando último acceso para user: " + username);
        userService.updateLastLoginAccess(username, new Date());
    }   
}

This is successfully created in context, according to the Spring debug messages.

DEBUG DefaultListableBeanFactory:448 - Creating instance of bean 'authenticationSuccessEventHandler'
2016-12-11 11:33:29 DEBUG InjectionMetadata:72 - Registered injected element on class [listeners.AuthenticationSuccessEventHandler]: AutowiredFieldElement for private services.UserService listeners.AuthenticationSuccessEventHandler.userService

When I authenticate correctly in the application, no event is released by Spring Security and therefore this Event Listener is not called.

My Spring Security configuration is this

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder() {
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/signup").anonymous()
                .antMatchers("/admin/**").authenticated()
                .anyRequest().permitAll()
                .and()
                .formLogin().loginPage("/admin/login").permitAll()
                .usernameParameter("username").passwordParameter("password")
                .and()
                .logout()
                    .logoutRequestMatcher(new AntPathRequestMatcher("/admin/logout"))
                    .logoutSuccessUrl("/admin/login?logout")
                .and()
                .exceptionHandling().accessDeniedPage("/403")
                .and()
                .csrf();
    }
}

SecurityWebApplicationInitializer

package config;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

/**
 *
 * @author sergio
 */
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

}

I am using Spring Security 4.2.0.RELEASE.



Solution 1:[1]

You may need to register the event-publishing infrastructure (eg. by configuring a DefaultAuthenticationEventPublisher).

@EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter {

    ...

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .authenticationEventPublisher(authenticationEventPublisher())
            .userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder());
    }   

    @Bean
    public DefaultAuthenticationEventPublisher authenticationEventPublisher() {
        return new DefaultAuthenticationEventPublisher();
    }
}

Solution 2:[2]

This is how i achieved it.

1) In your Application class, expose your application listener like

@Bean
public ApplicationListener applicationListener(){
    return new AuthSuccessApplicationListener();
}

2) Implement AuthSuccessApplicationListener for example

public class AuthSuccessApplicationListener implements 
ApplicationListener<InteractiveAuthenticationSuccessEvent>{

@Autowired(required=false)
HttpSession httpSession;

@Autowired
Environment env;

/**
 * Handle an application event.
 *
 * @param appEvent the event to respond to
 */
@Override
public void onApplicationEvent(InteractiveAuthenticationSuccessEvent appEvent) {

    if (appEvent!=null) {
        LdapUserDetailsImpl ldapUserDetailsImpl = (LdapUserDetailsImpl) appEvent.getAuthentication().getPrincipal();
    try {
            if (ldapUserDetailsImpl != null) {

                logger.info("Session Created for " + ldapUserDetailsImpl.getUsername());

                if (httpSession.getAttribute("adminUser") == null) {
                    // check user is admin and set into session
                    if (isAdminUser(ldapUserDetailsImpl.getUsername())) {
                        httpSession.setAttribute("adminUser", "ADMIN_USER");
                        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
                        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(auth.getAuthorities());
                        // Add the ROLE_ADMIN into Authorities
                        authorities.add(new SimpleGrantedAuthority(SecurityConfig.ADMIN));
                        // Create a new Authentication based on current principal and authorities and set into Security Context
                        Authentication newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(), auth.getCredentials(), authorities);
                        SecurityContextHolder.getContext().setAuthentication(newAuth);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("Exception occurred : " + e.getMessage());
        }
    }
}

Solution 3:[3]

Here's how the Spring Security docs explain it (at the time of writing, Spring Security is at version 5.6.1):

To listen for these events, you must first publish an AuthenticationEventPublisher. Spring Security’s DefaultAuthenticationEventPublisher will probably do fine:

@Bean
public AuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher appEventPublisher) {
  return new DefaultAuthenticationEventPublisher(appEventPublisher);
}

See https://docs.spring.io/spring-security/reference/servlet/authentication/events.html

Solution 4:[4]

In spring-security version 5.6.0 only UsernamePasswordAuthenticationFilter fires InteractiveAuthenticationSuccessEvent. As option You may extends AbstractAuthenticationProcessingFilter or do it by own a success handler implementation. Example:

class LoggingAuthenticationSuccessHandler extends WebFilterChainServerAuthenticationSuccessHandler {

    @Autowired
    private AuthenticationEventPublisher eventPublisher;


    @Override
    public Mono<Void> onAuthenticationSuccess(WebFilterExchange webFilterExchange, Authentication authentication) {
        this.eventPublisher.publishAuthenticationSuccess(authentication);

        return super.onAuthenticationSuccess(webFilterExchange,authentication);
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Willi Mentzel
Solution 2 Kul Bhushan Prasad
Solution 3 Brice Roncace
Solution 4 cane