SpringSecurity: Capturing RememberMe Success Event

Umar Ashfaq
Eastros
Published in
3 min readJul 10, 2012

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.
[sourcecode language=”java”]
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
}
}
[/sourcecode]
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:
[sourcecode language=”xml”]
<! — 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>
[/sourcecode]
Comments are welcome.

--

--