Sunday, March 21, 2010

GWT + Spring Security

Spring security is matured framework for the authentication and GWT is the modern framework to provide a rich internet application. In my project I am using both of these project.But while integrating both of these framework I face some problems and would like to share with you.

To understand the problem we should know how spring security handle security exception. In spring we can classify exceptions into authentication exception + authroization exception. To handle spring security exception spring provide exceptiontranslationfilter. When exceptiontranslationfilter catches authentication excetion it uses authenticationentrypoint property and default implementation of authenticationentrypoint redirect user to login page. When exceptiontranslationfilter catches authorization exception it uses accessDeniedHandler property and default implementation of accessDeniedHandler redirect user to access denied page. Spring also provide ConcurrentSessionFilter which stop user from multiple login and if detect multiple login it also redirect to session expired date.

Now above the discussion it is clear that spring filters redirect user in case of exception. But in gwt all rpc call for server are ajax call and if the ajax call triggers security exception we cann't use spring default behavior which redirect user. So how to handle them.

There is one more thing, in gwt all the server call are handled by RemoteServiceServlet and this servlet catches all the exception and serialize them in the gwt serialize format. So exceptiontranslationfilter will not catches any exception.And client will only get status code exception and client code won't be able to display proper message to user.

To solve issue related to RemoteServiceServlet I override its doUnexpectedFailure method and check if the reason of this exception is SpringSecurityException and if it is SpringSecurityException I just throw SpringSecurityException from doUnexpectedFailure method instead to serializing them.

In FilterChainProxy bean I created two filter chains, one to handle RPC calls and one to handle non RPC call. For all the gwt rpc call I wrote custom implementation of concurrentSessionFilter and exceptionTranslationFilter. In the custom implementation of these filter re- throw these exceptions. And to handle all these exception I add one new filter above the springSecurityFilterChain in web.xml and this filter intercepts all gwt rpc calls. This filter catches all the security exception and serializes them.

public class SecurityExceptionWrapperFilter implements Filter
{
private FilterConfig filterConfig;

public void destroy()
{
// TODO Auto-generated method stub
}

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
{
if (req instanceof HttpServletRequest && res instanceof HttpServletResponse)
{
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try
{
chain.doFilter(req, res);
}
catch (com.custom.security.exception.SecurityException e)
{
try
{
writeResponse(request, response, serializeException(e));
}
catch (SerializationException ex)
{ RPCServletUtils.writeResponseForUnexpectedFailure(filterConfig.getServletContext(), response, ex);
}
catch (IOException ex)
{
RPCServletUtils.writeResponseForUnexpectedFailure(filterConfig.getServletContext(), response, ex);
}
}
}
else
{
chain.doFilter(req, res);
}
}

public void init(FilterConfig filterConfig) throws ServletException
{
this.filterConfig = filterConfig;
}

public String serializeException(Exception e) throws SerializationException
{
final Map, Boolean> whitelist = new HashMap, Boolean>();
whitelist.put(SecurityException.class, Boolean.TRUE);
whitelist.put(RuntimeException.class, Boolean.TRUE);
whitelist.put(Exception.class, Boolean.TRUE);
whitelist.put(Throwable.class, Boolean.FALSE);
whitelist.put(com.custom.security.exception.SecurityException.class, Boolean.TRUE);
whitelist.put(com.custom.security.exception.AuthenticationException.class, Boolean.TRUE);
whitelist.put(com.custom.security.exception.AuthorizationException.class, Boolean.TRUE);
whitelist.put(com.custom.security.exception.SessionExpiredException.class, Boolean.TRUE);
StandardSerializationPolicy serializationPolicy = new StandardSerializationPolicy(whitelist);
return RPC.encodeResponseForFailure(null, e, serializationPolicy);
}

private void writeResponse(HttpServletRequest request, HttpServletResponse response, String responsePayload)
throws IOException
{
boolean gzipEncode = RPCServletUtils.acceptsGzipEncoding(request)
&& shouldCompressResponse(request, response, responsePayload);
RPCServletUtils.writeResponse(filterConfig.getServletContext(), response, responsePayload, gzipEncode);
}

protected boolean shouldCompressResponse(HttpServletRequest request, HttpServletResponse response, String responsePayload)
{
return RPCServletUtils.exceedsUncompressedContentLengthLimit(responsePayload);
}

}


1 comment:

Coldfusion CFTHROW

Are you using CFThrow to throw custom exception in ColdFusion ? Wait. Read this article before using CFThrow in your application otherwis...