2014-11-06 181 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