Exploiting Spring MVC interceptors

Interceptors are a pretty nifty feature in Spring MVC and we mix it with annotations to pull off some cool things in our app that keeps our code neat and tidy.

Let us take the case of protecting your actions with CSRF tokens. One approach that you can take is, in every controller, the first thing that you do is CSRF token matching and sending a forbidden response if the tokens do not match. Being the technically savvy reader that you are, I am sure you see the problem with this approach. Your CSRF related code is littered all over the place and duplicated in each and every controller action. So how can we stick to the DRY principle and keep our code sane? How about moving that logic to a Spring MVC interceptor and configuring every request that hits the app to be intercepted by this? Now we have moved our whole CSRF processing to a single place, but there is a problem here. Say, you do not want some controller action to be checked for a CSRF token, how do you achieve this? Annotations to the rescue. Let us define a custom annotation called Csrf with an attribute called exclude. In the interceptor, we get the target controller method, check whether there is an annotation defined for the method and if there is an annotation, we skip the CSRF token check.

Interceptor code, HandlerMethod is the target method of the controller:

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    //To make sure this is a spring controller request
    if (handler instanceof HandlerMethod) {
        if (shouldCheck(request, (HandlerMethod)handler)) {
            String sessionToken = CSRFTokenManager.getTokenFromSession();
            String requestToken = csrfTokenManager.getCSRFToken(request);
            if (sessionToken.equals(requestToken)) {
                return true;
            } else {
                response.sendError(HttpServletResponse.SC_FORBIDDEN, "Bad or missing CSRF value");
                return false;
            }
        }
    }

    return true;
}

private boolean shouldCheck(HttpServletRequest httpServletRequest, HandlerMethod handlerMethod) {
    Csrf csrf = handlerMethod.getMethodAnnotation(Csrf.class);

    if (csrf != null) {
        return !csrf.exclude();
    } else {
        if ("POST".equals(httpServletRequest.getMethod())) {
            return true;
        }

        if ("GET".equals(httpServletRequest.getMethod())) {
            return false;
        }
    }

    return false;
}

Csrf annotation class:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Csrf {
    boolean exclude() default false;
}

An example controller with annotation usage:

	@Csrf(exclude=true)
	@RequestMapping(value = "foo", method = RequestMethod.POST)
	public ModelAndView foo(Model model,
			HttpServletRequest request, HttpServletResponse response) {

We use a similar pattern for our login validation also, will expound on this in a separate post. Are you looking forward to hacking on cool things like this? Get in touch with us at devgigs@freecharge.com, we are always looking for curious people who want to build beautiful products.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s