Pages

Thursday, April 10, 2014

Using ServletFilter to authenticate a user's Session

Servlet Filters are interceptors for requests and responses to transform or use the information in the request or response. Filters normally do not create a response themselves, instead, they provide global functions that can be attached to any web resources.

Filters are very important for a number of reasons. First of all, they provide a modular way to create units of functionality that can be reused, and secondly they can be used to transform the data in request or response.

Filters can perform manay different types of functions, including but not limited to :

  • Authenticating the requests
  • Logging and auditing
  • Data Transformation
  • Data Compression
  • Localization
In this example I am going to try to explain Filters by developing a simple filter that verifies that the user still has a valid session. If the session is expired then the Filter will block the request from going any further and redirect the user to the login page.

Let’s base our Filter on the following assumptions

  • We have a session bean called UserBean
  • The bean is stored in the session as user
  • The login page authenticates the user
  • The login page creates and stores a UserBean in session
  • We will always have a user variable in session
Based on the following assumptions, all we need to do is to make sure that we have an instance of UserBean in the session variable user with some valid user credentials.

This is what my Servlet Filter looked like when I first created it. All we need to do is to implement the Filter interface and override the doFilter() method. Whatever we want to do with that filter would be provided in this method.


public class SessionFilter implements Filter {

 @Override
 public void doFilter(ServletRequest req, ServletResponse res,
   FilterChain chain) throws IOException, ServletException {

  String sessionExpired = "/mywebproject/sessionExpired.html";

  try {

   HttpServletRequest request = (HttpServletRequest) req;
   HttpServletResponse response = (HttpServletResponse) res;

   HttpSession session = request.getSession(false);
   if(null == session){
   
    request.getRequestDispatcher("/sessionExpired.html").forward(request, response);
    
   }else{
   
    UserBean ub = (UserBean)session.getAttribute("user");
    if(null == ub){
    
     response.sendRedirect(sessionExpired);
     
    }else if (ub.getUserID() == null){
    
     response.sendRedirect(sessionExpired);
    }
   }

  } catch (Exception f) {
   f.printStackTrace();
  }
  chain.doFilter(req, res);
 }
}

Here we are simply checking for the session if we have a valid UserBean object in the session. If we have an object in the session then we validate the object if it contains a user id. If everything is fine then we continue the process and pass the process flow to the next filter in the chain. If not then redirect the control to the session expired page to let the users know that the session is no longer valid and they have to login to the system again.

Ignored Resources:
There is one problem though; this filter was not working because it was redirecting even the login page to the session expired page. This is because the filter is invoked every time we send a request to the server and a request to the login page will never have a user object in the session on our first request. As a result the system becomes un-usable.

There are a few ways to deal with this issue; the easiest would be to get the context path from the request and avoid any session checks if the context path contains the name of your login page.

The clean way however is to provide a list of ignored resources in the web.xml configuration file. This is what the configuration file looks like when we provide ignored resources:


 <filter>
     <filter-name>SessionFilter</filter-name>
     <filter-class>com.mywebproject.common.filter.SessionFilter</filter-class>
     <init-param>
         <param-name>avoid-urls</param-name>
         <param-value>login</param-value>
     </init-param>     
 </filter>
 <filter-mapping>
     <filter-name>SessionFilter</filter-name>
     <url-pattern>/pages/*</url-pattern>
 </filter-mapping>
 

The avoid-urls parameter tag initializes the Filter with this list of URLs that the filter will avoid. Unfortunately this is simply an indicator here and you still need to provide an implementation for the filter to avoid the URLs provided in this list.

Note: You can provide more than one comma separated URLs in the param-value parameter.

Now we need to provide an implementation to avoid these URLs in our Filter. This is how the updated SessionFilter looked like after adding the functionality to load and then avoid the URLs provided in the web.xml configuration file.


package com.mywebproject.common.filter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.StringTokenizer;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.mywebproject.user.bean.UserBean;

public class SessionFilter implements Filter {
 
 private ArrayList<String> urlList;

 @Override
 public void init(FilterConfig config) throws ServletException {

  urlList = new ArrayList<String>();
  String urls = config.getInitParameter("avoid-urls");
  StringTokenizer token = new StringTokenizer(urls, ",");
  
  while (token.hasMoreTokens()) {
   urlList.add(token.nextToken());

  } 
 }
 
 @Override
 public void doFilter(ServletRequest req, ServletResponse res,
   FilterChain chain) throws IOException, ServletException {

  String sessionExpired = "/mywebproject/sessionExpired.html";

  try {

   HttpServletRequest request = (HttpServletRequest) req;
   HttpServletResponse response = (HttpServletResponse) res;
   
   String url = request.getServletPath();
   boolean allowedRequest = false;
   for (String allowed: urlList) {
    
    if(url.contains(allowed)){
     allowedRequest = true;
     break;
    }
   }

   if(!allowedRequest){

    HttpSession session = request.getSession(false);
    if(null == session){
    
     request.getRequestDispatcher("/sessionExpired.html").forward(request, response);
     
    }else{
    
     UserBean ub = (UserBean)session.getAttribute("user");
     if(null == ub){
     
      response.sendRedirect(sessionExpired);
      
     }else if (ub.getUserID() == null){
     
      response.sendRedirect(sessionExpired);
     }
    }

   }

  } catch (Exception f) {
   f.printStackTrace();
  }
  chain.doFilter(req, res);
 }
}

This Filter will now avoid checking for a valid session for any requests for the login page. Other than that page, it will always look for a valid session and a valid logged in user. If the session is expired or if there is no valid UserBean in the session then the Filter will not allow the request to go ahead and will redirect the user to the login page.

It is just one example of how the filters can be used. As mentioned in the beginning of this post, the filters can be used for all sorts of tasks that you want to perform on all or probably most of requests coming to your server.

No comments:

Post a Comment