【问题标题】:@EventListener for AuthenticationSuccessEvent or InteractiveAuthenticationSuccessEvent not fired未触发 AuthenticationSuccessEvent 或 InteractiveAuthenticationSuccessEvent 的 @EventListener
【发布时间】:2017-04-25 21:09:18
【问题描述】:

我在 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());
    }   
}

这是在上下文中成功创建的,根据 Spring 调试消息。

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

当我在应用程序中正确验证时,Spring Security 不会释放任何事件,因此不会调用此事件侦听器。

我的 Spring Security 配置是这样的

@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 {

}

我正在使用 Spring Security 4.2.0.RELEASE。

【问题讨论】:

    标签: spring spring-mvc spring-security


    【解决方案1】:

    以下是 Spring Security 文档的解释方式(在撰写本文时,Spring Security 的版本为 5.6.1):

    要侦听这些事件,您必须首先发布 AuthenticationEventPublisher。 Spring Security 的 DefaultAuthenticationEventPublisher 可能会很好:

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

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

    【讨论】:

      【解决方案2】:

      您可能需要注册事件发布基础架构(例如,通过配置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();
          }
      }
      

      【讨论】:

      • 你可以省略@Configuration,因为@EnableWebSecurity已经申请了
      【解决方案3】:

      这就是我实现它的方式。

      1) 在您的 Application 类中,像这样公开您的应用程序侦听器

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

      2) 以实现 AuthSuccessApplicationListener 为例

      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());
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2016-09-03
        • 1970-01-01
        • 2021-03-22
        • 1970-01-01
        • 2018-05-31
        • 1970-01-01
        • 2023-01-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多