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:

Sunday, August 5, 2012

Java class for Valums AJAX file uploader for IE9 with spring MVC

I wanted to have AJAX file upload which supports multiple files uploading for my recent project. The Valums AJAX file up-loader gave me a very good experience and integrated that into my application. This file up-loader uses XHR for uploading files on the browsers which supports XMLHttpRequest level 2 and falls back to hidden iframe based upload in other browsers. 

My application should run in FF, Chrome and IE. IE9 does not support XHR file uploads and even Valums GitHub has only provided a java example for XHR Level 2 supporting browsers which directly reads the input stream from the request. 

For browsers which do not support XHR Level 2, up-loader send the file to server as 'multipart/form-data'. Therefor, we should write our up-loader java controller class so that it reads multipart form data when request coming from browsers which do not support XHR file upload (like IE9) and directly read the input stream for the requests coming from the browsers which support XHR file uploads (like FF3.6+, Safari4+, Chrome). 

In both type of browsers, we should make sure that the response type as 'text/plain'. My application is running with spring 3 MVC which persuaded me to do some work around when reading multipart form data from the request. In spring 3 MVC environment, 'HttpServletRequest' which has 'multipart/form-data' are wrapped into 'MultipartHttpServletRequest', not like in normal servlet environment. 

Here, I am going to give you an example and basic java code which reads multipart form data with spring 3 MVC environment. If you are not using, spring 3 MVC and using some other controllers like pure servlets or struts 2 action class, you can directly read the multipart form data by using HttpServletRequest. 

However, for Valums file up-loader, we should write our controller class in tow conditional way. One is for XHR file upload supporting browsers and one for 'multipart/form-data' based file uploading browsers.

@RequestMapping(value = "*ajax*", method = RequestMethod.POST)
public @ResponseBody
String uploadFile(HttpServletRequest request, HttpSession session, HttpServletResponse response, Principal principal, Model model, Locale locale) throws IOException, ServletException {

   InputStream is = null;
   String filename = null; 
   String result = null; 
   try {
      
       if (isMultipartContent(request)) { 
            MultipartHttpServletRequest mrequest = (MultipartHttpServletRequest)request;
            Map<String MultipartFile> fileMap = mrequest.getFileMap();           
            for (Map.Entry<String MultipartFile> entry : fileMap.entrySet()) {
               MultipartFile mfile = entry.getValue(); 
               is = mfile.getInputStream();
               filename = mfile.getOriginalFilename();               
               break;
            }
       } else {
           filename = request.getHeader("X-File-Name");
           is = request.getInputStream();
       }
      
       result = "{success:true}";

   } catch (Exception ex) {
       ex.printStackTrace();
       result = "{success:false}";  
   } finally {
       try {
    is.close();
       } catch (IOException ignored) {}
   } 
   return result;
}


private static final boolean isMultipartContent(HttpServletRequest request) {
    String contentType = request.getContentType();
    if (contentType == null) {
     return false;
    }
    if (contentType.toLowerCase().startsWith("multipart/")) {
     return true;
    }
    return false;
}


I hope this will help you. At the beginning, even I thought, Valums ajax file up-loader does not work in IE9. But it works fine in IE as well. If you need any help, feel free to put comment or send a mail to me.
You may also like:
Share

Widgets