SpringSecurity: Capturing RememberMe Success Event

I really like Spring Security because with very little configuration, you can add a bunch of powerful features to your application. Form based logins, auto-logins based upon remember me cookies, password hashing, session management, powerful access control to name a few. For basic purposes, you don’t even need to know how these things actually work because Spring Security hides all of these complexities behind a few simple XML configuration tags.

However moving towards more advanced operations could become a daunting task. I needed to add some logs in database each time user would auto-login using Remember Me cookie. This seemingly simple task actually took me countless hours, and drove me to explore deeper down in the framework. Eventually I had to override most of the default configurations with my custom implementations. Here I will try to share them as simple as possible.

Spring Security actually has a chain of filters that perform different operations each time an HTTP request reaches the application. One of them is RememberMeAuthenticationFilter. RememberMeAuthenticationFilter has a method onSuccessfulAuthentication which is invoked if a user is successfully auto-logged in using a remember me cookie. So solution to our problem is to extend RememberMeAuthenticationFilter and override onSuccessfulAuthentication.

public class MyRememberMeAuthenticationFilter extends
		RememberMeAuthenticationFilter {
	@Override
	protected void onSuccessfulAuthentication(HttpServletRequest request,
			HttpServletResponse response, Authentication authResult) {
		// TODO: User has been auto-logged using a remember me cookie, do your stuff here
	}
}

Simple as cake, no?

Don’t be deceived, you haven’t yet told Spring Security about your new filter. This is the point where complexity begins. Here is what you need to do now:

<!-- Yes, auto-config needs to switch off, otherwise you will get loads of errors. -->
<http auto-config="false">
    <http-basic/>
    <session-management session-fixation-protection="none"/>			
	
    <!-- Since we are using our custom implementation of UsernamePasswordAuthenticationFilter, we need to make sure that we don't use <form-login/> element. -->	
    <custom-filter ref="myUsernamePasswordAuthenticationFilter" position="FORM_LOGIN_FILTER"/>
    
    <!-- Similarly we need to remove <remember-me/> element if we have any. -->
    <custom-filter ref="myRememberMeAuthenticationFilter" position="REMEMBER_ME_FILTER"/>
    
    <!-- And also <logout/> element if we have any. -->
    <custom-filter ref="myLogoutFilter" position="LOGOUT_FILTER"/>		
</http>

<!-- 'alias' is like bean id, with which you can refer following AuthenticationManager to other beans. -->
<authentication-manager alias="authenticationManager">
    <!-- AuthenticationProvider for form based authentication. 'user-service-ref' should refer to a bean, which implements UserDetailsService. -->
    <authentication-provider user-service-ref="userManager">
        <password-encoder hash="md5"><salt-source user-property="creationDate"/></password-encoder>
    </authentication-provider>		
    <!-- This one is for remember me authentication. -->
    <authentication-provider ref="myRememberMeAuthenticationProvider"/>			
</authentication-manager>	

<!-- MyRememberMeServices extends TokenBasedRememberMeServices class. Nothing needs to be overridden here. In fact, you could directly use TokenBasedRememberMeServices or any other RememberMeServices here. -->
<beans:bean id="myRememberMeServices" class="com.emumba.sample.security.MyRememberMeServices">
    <!-- Game of Thrones rocks \m/ -->
    <beans:property name="key" value="HEAR_ME_ROAR"/>
    <beans:property name="userDetailsService" ref="userManager"/>
</beans:bean>
	
<beans:bean id="myRememberMeAuthenticationFilter" class="com.emumba.sample.security.MyRememberMeAuthenticationFilter">
    <beans:property name="rememberMeServices" ref="myRememberMeServices"/>
    <beans:property name="authenticationManager" ref="authenticationManager"/>
</beans:bean>

<!-- It is important to override default UsernamePasswordAuthenticationFilter, because we need to pass reference of myRememberServices to it, otherwise remember me cookie will never be created to client end. -->	
<beans:bean id="myUsernamePasswordAuthenticationFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
    <beans:property name="rememberMeServices" ref="myRememberMeServices"/>
    <beans:property name="authenticationManager" ref="authenticationManager"/>
    <!-- Haven't added bean definitions of myAuthenticationSuccessHandler and myAuthenticationFailureHandler because they are out of scope of this post. Besides, I think they are too simple to implement. -->
    <beans:property name="authenticationSuccessHandler" ref="myAuthenticationSuccessHandler"/>
    <beans:property name="authenticationFailureHandler" ref="myAuthenticationFailureHandler"/>
    <beans:property name="filterProcessesUrl" value="/j_spring_security_check"/>
</beans:bean>
<!-- Overriding default LogoutFilter is also important because we need to pass reference of myRememberMeServices here as well, otherwise remember me cookie will not be cleared on logout. -->
<beans:bean id="myLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
    <!-- Again, definition of LogoutSuccessHandler is beyond scope of this article. -->
    <beans:constructor-arg ref="myLogoutSuccessHandler"/>
    <beans:constructor-arg>
	<beans:array>
	    <beans:ref local="myRememberMeServices"/>
	    <beans:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
	</beans:array>
    </beans:constructor-arg>
</beans:bean>	
<beans:bean id="myRememberMeAuthenticationProvider" class="com.emumba.sample.security.MyRememberMeAuthenticationProvider">
    <!-- key needs to be same as that provided to myRememberMeServices. -->
    <beans:property name="key" value="HEAR_ME_ROAR"/>
</beans:bean>

Comments are welcome.

Umar Ashfaq

Umar Ashfaq is a full-stack web developer. His core strengths are Java (Spring, Hibernate stack) and JavaScript (both client-side and server-side). Follow him on twitter @umarashfaq87

More Posts

Follow Me:
TwitterFacebookLinkedIn

No related posts.

14 Comments

  • 1
    Programmer
    September 28, 2012 - 11:13 pm | Permalink

    Thanks for the good article. Can you please upload the full source code so that it is easier for us to debug and play around ?

    Thanks once again.

  • 2
    September 29, 2012 - 12:53 am | Permalink

    Yes sure, I will do it sometime soon.

  • 3
    Gabriel
    October 26, 2012 - 1:29 pm | Permalink

    Hello,

    I am implementing a simpler version of the “remember-me” feature, using Spring Security 2.
    I just added the and the checkBox in my jsf page. It all works pretty well, but when I close/reopen the browser and access the application’s webPage, the automatic login is done after the page is loaded and the authentication can be seen on refresh.
    Do you have any ideas?

    Thank you for your time.

    Gabriel

    • 4
      Gabriel
      October 26, 2012 - 1:31 pm | Permalink

      *I just added the “remember-me” tag

    • 5
      October 26, 2012 - 4:04 pm | Permalink

      Hi Gabriel, Im sorry I do not have experience in JSF so can’t help you there.

      • 6
        Gabriel
        October 26, 2012 - 4:15 pm | Permalink

        I don’t think it’s a JSF related issue, I don’t know how to give the RememberMeAuthenticationFilter a higher position than others

  • 7
    Bao Ngoc
    November 19, 2012 - 9:14 am | Permalink

    Sorry, but can you send me full source code

  • 8
    umar
    March 18, 2013 - 1:23 pm | Permalink

    Umar your work is great. It will be much helpful if you upload full example.
    Thanks,

  • 9
    Noushad Siddiqui
    April 18, 2013 - 4:41 pm | Permalink

    Hi Umar,

    If I am using custom UsernamePasswordAuthenticationFilter , Is it mandatory to pass custom rememberMeServices ?

    As of now I was using Simple Hash-Based Token Approach for remember me cookies, after using Custom UsernamePasswordAuthenticationFilter cookies are not generated.

    Please guide

    Thanks

    • 10
      Umar Ashfaq
      April 18, 2013 - 4:51 pm | Permalink

      Hi Noushad,

      It’s been quite a while since I worked on Spring Security, so I am bit out of context. Please ask the question on stackoverflow.com, they might help you better.

      • 11
        Noushad Siddiqui
        April 19, 2013 - 1:10 am | Permalink

        Thanks Umar,

        anyway I found some clue in reference doc,
        Yes I need to pass custom rememberMeService. Its working now

        Thanks
        Noushad

        • 12
          Umar Ashfaq
          April 19, 2013 - 1:26 am | Permalink

          Thanks for sharing experience, Noushad!

  • 13
    Sameer
    June 23, 2014 - 4:53 pm | Permalink

    Hi Umar,
    We have done something similar. But my controllers are getting executed before my custom authentication filter gets executed. When I saw in debug mode I can see that in filter chain authentication filter is later in the order. Is there any way to force its way up.

  • Leave a Reply

    Your email address will not be published. Required fields are marked *

    *

    Wrap your code in [sourcecode language="java"][/sourcecode] tag (you can replace "java" with language you are using).