Friday, August 24, 2012

Passing more parameters with login request - Java

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.
You may also like:

4 comments:

  1. http://static.springsource.org/spring-security/site/docs/3.0.7.RELEASE/reference/remember-me.html

    http://static.springsource.org/spring-security/site/docs/3.0.7.RELEASE/apidocs/org/springframework/security/web/authentication/RememberMeServices.html

    ReplyDelete
  2. @Gireesh: Yes, Gireesh, I know this. But, I wanted to use my own login.jsp page,not the standard one created by spring itself. And also,not using 'UserDetailsService' and I am using my own authentication provider.

    ReplyDelete
  3. Hi,

    You seems to have reinvented wheel,

    _spring_security_remember_me could be directly mapped to check box in login form like j_username.

    ReplyDelete
  4. Hi,

    when I start jetty I'm getting:
    WARN o.s.s.c.h.DefaultFilterChainValidator - Possible error: Filters at position 3 and 4 are both instances of org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

    Any idea?

    Thanks

    ReplyDelete

Share

Widgets