Tuesday, February 1, 2011

The Power Of Filters

Well people, I am not going write about any other filters, but this post is about servlet filters, commonly called as intercepting filters. If you wish to know what filters are, here is a good article: Read this

By the word 'Power', I want to highlight the the uses and advantages of filters by showing an example in a real world web application. Let me list down its uses before I go ahead in detail
  • Restricting unauthorized access to session bound resources
  • If user has active session, restrict user to navigate to home page unless he/she is activated
  • Implementing single sign on feature
I'd like to make a point here. When a user is familiar with your web app, he will be aware of certain pages being displayed. And, if the user is mischievous, that damn guy will play around the URL of your browser like hell! If you are not using filters, there will be breach when there is no session! Obviously, you don't want that to happen at any cost!

I presume that all developers will be using the below resources/pages in their web application:
  1. Login.jsp, LoginActionServlet, LogoutActionServlet
  2. UserRegistration.jsp, UserRegistrationActionServlet
  3. UserActivation.jsp, UserActivationActionServlet
  4. AuthorizationFailed.jsp, SystemDownError.jsp
  5. All JSP & servlet resources which are bound by session like; Home.jsp, UserProfile.jsp, ChangePassword.jsp, etc
  6. Other files like; images (*.png, *.jpg, *.gif), java scripts (*.js), CSS (*.css)

Lets directly walk through the code of the filter which I use and examine it. Let me brief you people about what my filter does considering the points below:
  1. When the user logs in or registers himself successfully, user session is created,  he should activate his account by providing activation code sent to his e-mail (UserActivation.jsp). He cannot access Home.jsp unless activated.
  2. If user session exists and he is not activated yet and, he tries to access any other resource by changing the URL, he is redirected back to UserActivation.jsp.
  3. If user session exists and he is activated, and he tries to access any of Login.jsp, UserRegistration.jsp, UserRegistrationActionServlet or LoginActionServlet, he is redirected to Home.jsp (Single sign on feature :) ). This point comes in handy when user opens tabs on the same browser, he will be redirected to home page.
  4. If no session exists, and user is naughty enough to access any of session bound resources, show him the door out by redirecting him to AuthorizationFailed.jsp (Restricting unauthorized access to session bound resources)
Now, keeping above points in mind, define init-paramaters for our filter.
  1. avoid-urls: LogoutActionServlet, Logout.jsp, AuthorizationFailed.jsp, SystemDownError.jsp, .js, .css, .png, .jpg, .gif. All these resources can be accessed any time, whether or not session exists or not.
  2. activate-user: UserActivation.jsp, UserActivationActionServlet.  These resources can be accessed when user session exists and when user is not active.
  3. login-reg-page: Login.jsp, LoginActionServlet, UserRegistration.jsp, UserRegistrationActionServlet
  4. no-session: AuthorizationFailed.jsp. This page is used as a redirection target when no session exists.
  5. home-page: Home.jsp. The user home page. 
Coming to the implementation of the filter. I have used below class variables:

/* allowedURLlist: to store the value of param:     avoid-urls */
private List allowedURLlist;
/* activateUserURLlist: to store the value of param:     activate-user */
private List activateUserURLlist;
/* activateUserPage: to store the value:   
  UserActivation.jsp */
private String activateUserPage; 
/* forbiddenPage: to store the value: 
  Authorizationfailed.jsp */
private String forbiddenPage;

/* homePage: to store the value: 
  Home.jsp */
private String homePage;

/* loginPageList: to store the value of param: 
  login-reg-page */

private List loginPageList;

/* activateAndLoginPages: to store the value of params:    login-reg-page and activate-user */

private List activateAndLoginPages;

Read all the init-parameters in init() and store all the comma separated values of parameters corresponding lists

Once the application is up, this method is called only once.

public void init(FilterConfig fConfig) throws ServletException {

String urls = fConfig.getInitParameter("avoid-urls");
String activateUserPages = fConfig.getInitParameter("activate-user");
forbiddenPage = fConfig.getInitParameter("no-session");
homePage = fConfig.getInitParameter("home-page");
String loginPages = fConfig.getInitParameter("login-reg-page");

loginPageList = new ArrayList();
allowedURLlist = new ArrayList();
activateUserURLlist = new ArrayList();
activateAndLoginPages = new ArrayList();

StringTokenizer token = new StringTokenizer(urls, ",");
String currentToken = null;

while (token.hasMoreTokens()) {
allowedURLlist.add(token.nextToken().trim());
}

token = new StringTokenizer(activateUserPages, ",");

while (token.hasMoreTokens()) {
currentToken = token.nextToken().trim();
activateUserURLlist.add(currentToken);
activateAndLoginPages.add(currentToken);
}
activateUserPage = activateUserPages.split(",")[0].trim();

token = new StringTokenizer(loginPages, ",");

while (token.hasMoreTokens()) {
currentToken = token.nextToken().trim();
loginPageList.add(currentToken);
activateAndLoginPages.add(currentToken);
}
}

Implement the filter logic in doFilter()

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {


boolean allowURL = false;
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpServletResponse httpResp = (HttpServletResponse) response;
HttpSession session = httpReq.getSession(false);
final String forbiddenURL = httpReq.getContextPath()+ forbiddenPage.trim();
/* Get the request URL */
String url = httpReq.getServletPath();


for (Iterator iterator = allowedURLlist.iterator(); iterator.hasNext();) {


String eachResource = iterator.next();
if (url.endsWith(eachResource)) {
allowURL = true;
/* Granted access to url */
break;
}
}
if (!allowURL) {
/* Checking for active user session... */
if (session != null && session.getAttribute("user") != null) {
/* User session exists.. */
StringBuffer userActivationURL = new StringBuffer(httpReq
.getContextPath());
userActivationURL.append(activateUserPage);
UserVO user = (UserVO) session.getAttribute("user");


if (!"Y".equals(user.getIsActive())) {
/* User is not activated, so add userActivation urls to   the allowed URL list */
for (int i = 0; i < activateUserURLlist.size(); i++)
allowedURLlist.add(activateUserURLlist.get(i));
/* User is inactive. Redirecting control to userActivationURL */
httpResp.sendRedirect(userActivationURL);
return;
} else {
/* User is already activated. Redirect to home page if user tries to access login page or activation page */
String homePageURL = httpReq.getContextPath()
+ homePage.trim();
for (Iterator iterator = activateAndLoginPages.iterator(); iterator.hasNext();) {
String page = (String) iterator.next();
if (url.endsWith(page)) {
/* Redirecting to home page */
httpResp.sendRedirect(homePageURL);
return;
}
}
}
} else {
/* If there is no active user session and user is trying to access pages other than welcome page or registration page, redirect control to forbidden URL */
for (Iterator iterator = loginPageList.iterator(); iterator
.hasNext();) {
String page = iterator.next();
if (!url.endsWith(page)) {
/* There is no active user session. Access to URL forbidden. Redirecting to AuthorizationFailed.jsp */
httpResp.sendRedirect(forbiddenURL);
return;
}
}
}
}
/* Calling doFilter() to proceed with URL */
chain.doFilter(request, response);
}
By now, I think you have realized the power of using filters :)