I am trying to find a way to persist a db record when a user authenticates via spring-security. Likewise, when they logout or timeout, I would like to update that record with that time. I have been trying to use AuthenticationSuccessHandler for logging in handling, and LogoutSuccessHandler for the logging out. But, when I use that, then my URL redirects after that seem to break.
Here is what I have so far:
@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());
}
}
and to trap log out events:
@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());
}
}
And I configure my security as such:
@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 );
}
}
If I put the .defaultSuccessUrl( "/" ) before the .successHandler then the handler is invoked, but the page redirect doesn't happen and the login results in a blank page of /login. Similarly for /logout.
Can anyone see what the issue is here
UPDATE: I added the Actuator and my own 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");
}
}
Now, when a login occurs, I get the messages: 2014-11-06 10:10:55.923 INFO 90807 --- [nio-9001-exec-7] o.s.b.a.audit.listener.AuditListener : AuditEvent [timestamp=Thu Nov 06 10:10:55 MST 2014, principal=admin, type=AUTHENTICATION_SUCCESS, data={details=org.springframework.security.web.authentication.WebAuthenticationDetails@21a2c: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 64320375B40CF959936E86F4D1F2973C}]
And I see the code executed. So if I can get to the AuditEvent, I will have the IP and timestamp for my logging. For logging out, I tried my own 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");
}
}
I also tried handling via listener:
@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");
}
}
}
neither of these calls are ever invoked. nor are there ever any messages to the console when the /logout call is made. I have a HttpSessionListener class that outputs a message on
public void sessionDestroyed(HttpSessionEvent arg0) {
totalActiveSessions--;
System.out.println("sessionDestroyed - deduct one session from counter: " + totalActiveSessions);
}
and that one is called, so I am sure the logout occurs.