UmYeah
UmYeah

Reputation: 820

Spring-Security: Call method after authentication

I'd like to track when users are logging in to my application. I have some code that I would like to execute right after the user is authenticated. The problem is, I can't figure out where this should be called. Does spring-security have a way to call a method after authentication?

Upvotes: 27

Views: 35091

Answers (7)

gjambet
gjambet

Reputation: 369

If you want to avoid reading all the thread : curated version with annotations & a bit more explanatory :

import org.springframework.context.ApplicationListener; 
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;

@Component 
public class LoginSuccessListener implements ApplicationListener<AuthenticationSuccessEvent{

    @Override
    public void onApplicationEvent(AuthenticationSuccessEvent evt) {

        // if you just need the login
        String login = evt.getAuthentication().getName();
        System.out.println(login + " has just logged in");

        // if you need to access full user (ie only roles are interesting -- the rest is already verified as login is successful)
        User user = (User) evt.getAuthentication().getPrincipal();
        System.out.println(user.getUsername() + " has just logged in");

    } 
}

Upvotes: 3

natskvi
natskvi

Reputation: 135

Authentication does not necessarily imply successful login. A user can be successfully authenticated via, e.g., two-way SSL (X.509 certs), and still Spring Security will redirect you to an error page if session concurrency management is set up with max-sessions="1" and this is a second concurrent login attempt. If your setup is simple, without session concurrency control, you can assume login = authentication for all practical purposes. Otherwise, if you have, e.g., logic that records every successful login in a database, you will have to invoke this logic at the point of actual login, not at the point of authentication. One way (by no means optimal, subject to my limited understanding of the Spring Security framework) to do this is to implement your own ConcurrentSessionControlAuthenticationStrategy (click here for the source code) and inject it into CompositeSessionAuthenticationStrategy in your Spring Security (3.2 and above) configuration XML:

<http>
    .
    .
    <session-management session-authentication-strategy-ref="sas" />
    .
    .
</http>
.
.
<beans:bean id="sas" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
    <beans:constructor-arg>
        <beans:list>
            <beans:bean class="path.to.my.implementation.of.ConcurrentSessionControlAuthenticationStrategy">
                <beans:constructor-arg ref="sessionRegistry"/>
                <beans:property name="maximumSessions" value="1"/>
                <beans:property name="exceptionIfMaximumExceeded" value="true"/>
            <beans:bean>
            <beans:bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy"/>
            <beans:bean class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy"/>
                <beans:constructor-arg ref="sessionRegistry"/>
            </beans:bean>
        </beans:list>
    </beans:constructor-arg>
</beans:bean>

<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>

I would have preferred to inject a custom PostLogin handler into the framework's ConcurrentSessionControlAuthenticationStrategy, instead of copy-pasting from it into my custom ConcurrentSessionControlAuthenticationStrategy and making modifications to it, but I do not know of a way to do this at the moment.

A more complete configuration example can be found here.

Upvotes: 1

Lee Chee Kiam
Lee Chee Kiam

Reputation: 12058

In case you want to continue with default behavior but just in between perform your own business logic, you may extend SimpleUrlAuthenticationSuccessHandler and invoke super.onAuthenticationSuccess(request, response, authentication); before you return. More details refer to https://stackoverflow.com/a/6770785/418439

Upvotes: 8

sab
sab

Reputation: 651

probably will be usefull for someone... In case of Spring 3, configure security:

<security:http use-expressions="true" auto-config="true">
    <security:intercept-url pattern="..."/>
    <security:form-login
            authentication-failure-handler-ref="authFailureHandler"
            authentication-success-handler-ref="authSuccessHandler"/>
    <security:logout success-handler-ref="logoutSuccessHandler"
            invalidate-session="true"/>
    <security:session-management session-fixation-protection="newSession"/>
</security:http>

<bean id="authFailureHandler" class="mine.AuthenticationFailureHandlerImpl"/>
<bean id="authSuccessHandler" class="mine.AuthenticationSuccessHandlerImpl"/>
<bean id="logoutSuccessHandler" class="mine.LogoutSuccessHandlerImpl"/>

and implement an appropriate class:

public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //do what you want with 
        response.getOutputStream().write("success".getBytes());
    }
}

You can link resources via that xml config.

Upvotes: 33

ams
ams

Reputation: 62652

Best way is to create an application listener and register it with the spring security context.

import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;

public class AuthenticationSuccessListener implements ApplicationListener<InteractiveAuthenticationSuccessEvent> {

    @Override
    public void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) {
        System.out.println("User Logged In");

    }
}

Make sure to add the above class the spring-security.xml as a bean. There are lots of other types of security events listeners you can listen to, check the type hierarchy for a list of all the types of security events you can listen to.

Upvotes: 16

manishpal
manishpal

Reputation: 394

This link Post Authentication Logic on ByteClip explains how to execute some logic after successful authentication without disturbing spring security filter chain

Upvotes: -1

Gandalf
Gandalf

Reputation: 9855

Just write your own SpringSecurityFilter and add it to the filter chain right after your authentication provider is called.

package my.code;

public class AuditFilter extends SpringSecurityFilter {

   public void doFilterHttp(...) throws ... {
      {application code to run before request is processed}
      chain.doFilter(...);
      {application code to run after request has fully processed} 
   }
}

Then in your configuration XML (wherever you setup the Security Filter chain) add a line like this:

<bean id="auditFilter" class="my.code.AuditFilter>
   <security:custom-filter position="LAST"/>  <-- you can change the position
</bean>

Upvotes: 4

Related Questions