Handling DWR Session timeouts

I recently had an issue in an application using Acegi and DWR, where even after the session invalidation the DWR calls could still be made. It seemed DWR calls were ignoring session timeouts. In order to over come this issue I wrote a little Ajax filter to check for session invalidation for DWR calls and throw an Exception if session has timed out. Throwing exception is more of a natural way of handling such scenarios then sending “plain/text” messages as recommended in DWR 2 API.

package myApp;

import java.lang.reflect.Method;
import org.directwebremoting.AjaxFilter;
import org.directwebremoting.AjaxFilterChain;
import org.directwebremoting.WebContextFactory;
import org.directwebremoting.extend.LoginRequiredException;

public class DwrSessionFilter implements AjaxFilter {
 public Object doFilter(Object obj, Method method, Object[] params, AjaxFilterChain chain) throws Exception {

  //Check if session has timedout/invalidated
  if (WebContextFactory.get().getSession(false) == null) {
   //Throw an exception
   throw new LoginRequiredException("This operation requires login.");
  }

  return chain.doFilter(obj, method, params);
 }
}

Then attach this filter with DWR bean:


    
        
        
    

In version 3.0RC-2 or later it is possible to have a global filter (<dwr:global-filter>) that can be used by all DWR beans.

Now after this all you need to do is catch the thrown LoginRequiredException on the client side.

dwr.engine.setErrorHandler(errorHandler);

function errorHandler(message, exception) {
 //Session timedout/invalidated
 if (exception.javaClassName == 'org.directwebremoting.extend.LoginRequiredException') {
  //Reload or display an error etc.
  document.location.reload();
 }
}

Here reloading the page was helpful because we were using Acegi (now known as spring security) and we had the FilterSecurityInterceptor setup to take care of the request. DWR guys also recommend something similar but that requires reverse Ajax and it also is not a natural way of handling this.

In Acegi it is also possible to have method-level role-based authorization for DWR calls using the MethodSecurityInterceptor. I will try to explain that in the next blog entry.

10 Comments

In order to use global filters with DWR 2.0.x with the DwrSpringServlet, the following needs to be done:1. Create a CustomAjaxFilterManagerpublic class CustomAjaxFilterManager extends DefaultAjaxFilterManager { List globalFilters = new ArrayList(); public void setGlobalFilters(List globalFilters) { this.globalFilters = globalFilters; } public CustomAjaxFilterManager() { for (AjaxFilter ajaxFilter : globalFilters) { addAjaxFilter(ajaxFilter); } }}2. Configure this in spring as follows, where CustomAjaxFilter is your global filter

Correction.The List should be initialized in an init method instead of the constructor. The init-method attribute should be correspondingly set in the spring xml.

thats great, thanx, I just tried the global filter and it worked perfectly…I added the filters in the setter method like this…public class MyAjaxFilterManager extends DefaultAjaxFilterManager { public void setAjaxFilters(List filters) { for( AjaxFilter af : filters ) { this.addAjaxFilter( af ); } }}

It is somehow not working for me. My tomcat always creates a new session object with the same jsessionid everytime the session times out.Even I am using acegi + spring. So the session is never null in the dwrSessionFilter.Any thoughts/comments??

@DRAre you passing false to getSession? like:WebContextFactory.get().getSession( false );Passing a "false" won't create the session. If you are getting the same session ID back that means your session is not expired. If for some reason your tomcat is recreating the session, then in dwrSessionFilter try using a condition based on some session attribute you set on login. like lets say "isSessionCreated":if(WebContextFactory.get().getSession( false )==null){ // normal session timeout}else if(session.getAttribute("isSessionCreated")==null){ // new session, but isSessionCreated is null because not set.}

thanks for the tip Kamran. I used the session.isNew() to get the same effect that you have described. I cannot use attributes set on login because I have the acegi remember me feature turned on which does not really go through the login controller.But now, the issue is that the LoginRequiredException is coming into my errorhandler as a java.lang.Throwable. In the dwr.log I see the following :WARN 30 Mar 10 14:43:12,481 impl.DefaultRemoter – Method execution failed: org.directwebremoting.extend.LoginRequiredException: User will have to re-logindwr.engine._remoteHandleException('1','0',{javaClassName:"java.lang.Throwable",message:"Error"}); WARN 30 Mar 10 14:43:12,481 dwrp.BaseCallMarshaller – –Erroring: batchId[1] message[org.directwebremoting.extend.LoginRequiredException: User will have to re-login]Has anyone come across this? how to get the same exception that DWR throws in our error handler

Leave a Reply