2014-11-06 194 views
2

我试图找到一种方法来保持数据库记录,当用户通过spring-security进行身份验证时。同样,当他们注销或超时时,我想在那段时间更新该记录。我一直试图使用AuthenticationSuccessHandler进行登录处理,并且LogoutSuccessHandler用于登出。但是,当我使用它时,那么我的URL在重定向后似乎会中断。春季安全登录/注销登录

这是我到目前为止有:

@Component 
public class MyLoginSuccessHandler implements AuthenticationSuccessHandler { 

    public MyLoginSuccessHandler() { 
     super(); 
    } 

    @Override 
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { 
     System.out.println("Logged In User " + (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()); 
    } 
} 

和陷阱注销事件:

@Component 
public class MyLogoutSuccessHandler implements LogoutSuccessHandler { 

    public MyLogoutSuccessHandler() { 
     super(); 
    } 

    @Override 
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) 
      throws IOException { 

     System.out.println("Logged OUT User " + (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()); 
    } 
} 

而且配置我的安全性,例如:

@Configuration 
@EnableWebMvcSecurity 
public class ApplicationSecurity extends WebSecurityConfigurerAdapter { 

    @Autowired 
    private CustomUserDetailsService customUserDetailsService; 

    @Autowired 
    private MyLogoutSuccessHandler myLogoutSuccessHandler; 

    @Autowired 
    private MyLoginSuccessHandler myLoginSuccessHandler; 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
http 
       .formLogin().failureUrl("/login?error") 
       .successHandler(myLoginSuccessHandler) 
       .defaultSuccessUrl("/") 
       .loginPage("/login") 
       .permitAll() 
       .and() 
       .logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")) 
       .permitAll(); 

     http 
       .sessionManagement() 
       .maximumSessions(1) 
       .expiredUrl("/login?expired") 
       .maxSessionsPreventsLogin(true) 
       .and() 
       .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) 
       .invalidSessionUrl("/"); 

     http 
       .authorizeRequests().anyRequest().authenticated(); 

     http 
       .logout() 
       .logoutSuccessHandler(myLogoutSuccessHandler) 
       .logoutSuccessUrl("/"); 
    } 


    @Override 
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
     PasswordEncoder encoder = new BCryptPasswordEncoder(); 
     auth.userDetailsService(customUserDetailsService).passwordEncoder(encoder); 
    } 
} 

如果我在.successHandler之前放置.defaultSuccessUrl("/"),然后处理程序被调用,但页面重定向不会发生,登录会导致/ login的空白页面。类似的/注销。

任何人都可以看到的问题是什么在这里

UPDATE: 我增加了执行器和我自己的ApplicationListener:

@Component 
public class LoginListener implements ApplicationListener<AuthenticationSuccessEvent> { 

    private static final Logger LOG = LoggerFactory.getLogger(LoginListener.class); 

    @Override 
    public void onApplicationEvent(AuthenticationSuccessEvent event) { 
     UserDetails ud = (UserDetails) event.getAuthentication().getPrincipal(); 

     LOG.info("User " + ud.getUsername() + " logged in successfully"); 
    } 
} 

现在,当登录时,我得到的消息: 2014-11-06 10:10:55.923 INFO 90807 --- [nio-9001-exec-7] osbaaudit.listener.AuditListener:AuditEvent [timestamp = Thu Nov 06 10:10:55 MST 2014,principal = admin, type = AUTHENTICATION_SUCCESS,data = {details = org.springframework.security.web.authentication.WebAuthenticat ionDetails @ 21a2c:RemoteIpAddress:0:0:0:0:0:0:0:1; SessionId:64320375B40CF959936E86F4D1F2973C}]

而且我看到执行的代码。所以如果我可以到达AuditEvent,我将拥有我的日志记录的IP和时间戳。对于注销,我想尽自己的LogoutHandler:

@Component 
public class MyLogoutHandler implements LogoutHandler { 

    private static final Logger LOG = LoggerFactory.getLogger(MyLogoutHandler.class); 

    @Override 
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { 

     User user = (User) authentication.getPrincipal(); 
     LOG.info("User " + user.getUsername() + " logged OUT successfully"); 

    } 
} 

我也试图通过监听处理:

@Component 
public class LogoutListener implements ApplicationListener<SessionDestroyedEvent> { 

    private static final Logger LOG = LoggerFactory.getLogger(LogoutListener.class); 

    @Override 
    public void onApplicationEvent(SessionDestroyedEvent event) { 
     List<SecurityContext> lstSecurityContext = event.getSecurityContexts(); 
     UserDetails ud; 
     for (SecurityContext securityContext : lstSecurityContext) 
     { 
      ud = (UserDetails) securityContext.getAuthentication().getPrincipal(); 
      LOG.debug("User " + ud.getUsername() + " logged OUT successfully"); 
     } 
    } 
} 

没有这些电话都是以往任何时候都调用。在拨打电话时也不会有任何消息传送到控制台。我有一个在

public void sessionDestroyed(HttpSessionEvent arg0) { 
     totalActiveSessions--; 
     System.out.println("sessionDestroyed - deduct one session from counter: " + totalActiveSessions); 
    } 

输出一条消息和一个被称为HttpSessionListener类,所以我肯定注销发生。

+2

见[此](http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-auditing.html)部的参考指南。添加'spring-boot-starter-actuator'依赖并实现你自己的'AuditEventRepository'。您需要额外的'LogoutHandler'来发布注销的审计事件(我怀疑),但这是您所需要的。 – 2014-11-06 07:02:49

+0

好主意。或者只是听'AuthenticationSuccessEvent'。但我不知道是否有注销事件。 – 2014-11-06 07:08:34

+0

@DaveSyer目前没有。见[gh-1836](https://github.com/spring-projects/spring-boot/issues/1836)和[SEC-2680](https://jira.spring.io/browse/SEC-2680) 。 – 2014-11-06 11:54:00

回答

0

我曾与注销后做额外的东西了同样的问题,并解决它通过以下方式:

  • 我用的,就像你在你的更新方法,一个ApplicationListener<SessionDestroyedEvent>
  • 但是,您还必须将HttpSessionEventPublisher添加到部署描述符中。原因描述在this页面。简而言之:
    会话销毁事件是Java EE HttpSessionEvent,发生在Spring环境之外。你可以用Java EE HttpSessionListener来捕捉它们。但是如果你想访问Spring相关的bean - f.e.春天的身份验证 - 那么你必须添加HttpSessionEventPublisher - 它转换的Java EE的事件将弹簧活动:

    <listener> 
        <listener-class> 
         org.springframework.security.web.session.HttpSessionEventPublisher 
        </listener-class> 
    </listener> 
    

在这之后,你可以赶上SessionDestroyedEvent这样的:

@Component 
public class AuthenticationApplicationListener { 

    @EventListener 
    public void handleSessionDestroyedEvent(SessionDestroyedEvent event) { 
     List<SecurityContext> lstSecurityContext = event.getSecurityContexts(); 
     for (SecurityContext securityContext : lstSecurityContext) { 

     //Try to find out, if this event is caused by a logout, 
     //This is true, when the old session has been an authenticated one. 
     Authentication auth = securityContext.getAuthentication(); 
     if (auth == null || 
      !auth.isAuthenticated() || 
      auth instanceof AnonymousAuthenticationToken) { 
      return; 
     } 

     //do something 
    } 
} 


与成功处理程序相比,此方法具有一些优势:

  • 它也在工作,当你使用程序注销(f.e.通过HttpServletRequest.logout()
  • 当您通过url使用注销时,它不会中断过滤器链。


也参见this answer