If you are using some container managed authentication mechanism like standard java form based authentication or spring security authentication mechanism, You are not really involving with user credential validation, like checking user name and password against the database, but the container is fully responsible for this. But what will happen if you want to pass some additional parameters with log-in details(username and password)?. The well known and most common scenario is passing "Keep me logged in" or "Remember me" check box value with your log-in details and doing some work with that while container is authenticating the user.
Recently, I had to implement the "Keep me logged in" function for one of my current project which are using spring 3 security as authentication mechanism. I am little new to spring 3 and it was challenging work for me of passing "Keep me logged in" check box status into spring's authentication provider class.
With this post, I will explain, How I achieved that.
The application uses 'AuthenticationProvider' class which extends from spring's 'AbstractUserDetailsAuthenticationProvider' and overrides 'retrieveUser' method which returns spring's UserDetails instance. Normally, authentication details are provided to 'retrieveUser' method via spring's 'WebAuthenticationDetails' instance.
Bellow shows the snip of code from 'retrieveUser' method of my authentication provider class.@Override protected User retrieveUser(String userName, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { OGGER.debug("Retrieve user : " + userName); final String password = authentication.getCredentials().toString(); WebAuthenticationDetails webAuthenticationDetails = authentication.getDetails()); try { User user = userBusiness.getUserByUsernameAndPassword(userName, password); logger.debug("Remote address : " + webAuthenticationDetails.getRemoteAddress()); logger.debug("Session Id : " + webAuthenticationDetails.getSessionId()); //................. return user; } catch (Exception e) { e.printStackTrace(); } }
I wanted to get "Keep me logged in" check box value into 'retrieveUser' method. It was very obvious that I am not able to get the check box value with current situation. The 'WebAuthenticationDetails' provides some details like remote address, session id etc, But not our own additional details.
As the next step, I implemented my own custom class by extending 'WebAuthenticationDetails' and put 'rememberMe' as a bean property.That class shows bellow.
package com.blimp.webapp.security; import javax.servlet.http.HttpServletRequest; import org.springframework.security.web.authentication.WebAuthenticationDetails; /** * @author semika * */ public class BlimpAuthenticationDetails extends WebAuthenticationDetails { private static final long serialVersionUID = 2012033417540858020L; private String rememberMe; public String getRememberMe() { return rememberMe; } //This constructor will be invoked by the filter public BlimpAuthenticationDetails(HttpServletRequest request) { super(request); this.rememberMe = request.getParameter("rememberMe"); } }
The next thing is, How we tell spring security engine to use my custom authentication detail class instead of using 'WebAuthenticationDetails' class when authenticating a user?.
For this one, we have to configure authentication processing filter in our spring security xml file. Some filtered contents from security XML file are shown bellow.<http auto-config="false"> <custom-filter ref="authenticationProcessingFilter" before="FORM_LOGIN_FILTER"/> <form-login login-page="/login.jsp?type=login" authentication-failure-url="/login.jsp?login_error=true" default-target-url="/getRedirectPage.htm"/> <intercept-url pattern="/**" access="ROLE_ADMIN,ROLE_USER" /> </http> <authentication-manager alias="authenticationManager"> <authentication-provider ref="daoAuthenticationProvider" /> </authentication-manager> <beans:bean id="authenticationProcessingFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"> <beans:property name="authenticationManager" ref="authenticationManager"/> <beans:property name="authenticationDetailsSource"> <beans:bean class="org.springframework.security.authentication.AuthenticationDetailsSourceImpl"> <beans:property name="clazz" value="com.blimp.webapp.security.BlimpAuthenticationDetails"/> </beans:bean> </beans:property> </beans:bean>
The 'daoAuthenticationProvider' is the instance of my 'AuthenticationProvider' class which extends 'AbstractUserDetailsAuthenticationProvider' and it has overridden 'retrieveUser' method. The my updated 'retrieveUser' method will be as follows.
@Override protected User retrieveUser(String userName, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { OGGER.debug("Retrieve user : " + userName); final String password = authentication.getCredentials().toString(); BlimpAuthenticationDetails webAuthenticationDetails = ((BlimpAuthenticationDetails) authentication.getDetails()); try { User user = userBusiness.getUserByUsernameAndPassword(userName, password); logger.debug("Remote address : " + webAuthenticationDetails.getRemoteAddress()); logger.debug("Session Id : " + webAuthenticationDetails.getSessionId()); logger.debug("Remember me : " + webAuthenticationDetails.getRememberMe()); //................. return user; } catch (Exception e) { e.printStackTrace(); } }
As you can see above, I can get the remember me check box value from 'retrieveUser' method.