Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make simple servlet filter work with @ControllerAdvice

I've a simple filter just to check if a request contains a special header with static key - no user auth - just to protect endpoints. The idea is to throw an AccessForbiddenException if the key does not match which then will be mapped to response with a class annotated with @ControllerAdvice. However I can't make it work. My @ExceptionHandler isn't called.

ClientKeyFilter

import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Controller

import javax.servlet.*
import javax.servlet.http.HttpServletRequest

@Controller //I know that @Component might be here
public class ClientKeyFilter implements Filter {

  @Value('${CLIENT_KEY}')
  String clientKey

  public void init(FilterConfig filterConfig) {}

  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
    req = (HttpServletRequest) req
    def reqClientKey = req.getHeader('Client-Key')
    if (!clientKey.equals(reqClientKey)) {
      throw new AccessForbiddenException('Invalid API key')
    }
    chain.doFilter(req, res)
  }

  public void destroy() {}
}

AccessForbiddenException

public class AccessForbiddenException extends RuntimeException {
  AccessForbiddenException(String message) {
    super(message)
  }
}

ExceptionController

@ControllerAdvice
class ExceptionController {
  static final Logger logger = LoggerFactory.getLogger(ExceptionController)

  @ExceptionHandler(AccessForbiddenException)
  public ResponseEntity handleException(HttpServletRequest request, AccessForbiddenException e) {
    logger.error('Caught exception.', e)
    return new ResponseEntity<>(e.getMessage(), I_AM_A_TEAPOT)
  }
}

Where I'm wrong? Can simple servlet filter work with spring-boot's exception mapping?

like image 678
Opal Avatar asked May 19 '15 20:05

Opal


People also ask

What is a filter in servlet?

A filter is an object that is invoked at the preprocessing and postprocessing of a request on the server, i.e., before and after the execution of a servlet for filtering the request. Filter API (or interface) includes some methods which help us in filtering requests.

How to avoid the need to change the Servlet on server side?

So to avoid this, we add Filters on the Server side. Note: The user-defined servlet filter is pluggable, i.e., its entry is defined in the web.xml file, if we remove the entry of the filter from the web.xml file, it will be removed automatically and we don’t need to change the servlet. Authentication and authorization of requests for resources.

How to use dofilter() method in servlet?

doFilter () method takes three arguments – ServletRequest, ServletResponse, FilterChain. With the help of FilterChain, we can forward the request after successful authentication. Create a class that implements the Filter interface and overrides all its methods, i.e., init (), doFilter (), destroy ().

What are the stages of a servlet?

These stages are similar to a servlet’s Instantiate, Initialize, Filter, destroy. The filter is used to pre-process the request and Post-processing the response. A Filter is a java object that performs the Filtering task on either the request to a resource or on the response from a resource or both.


1 Answers

As specified by the java servlet specification Filters execute always before a Servlet is invoked. Now a @ControllerAdvice is only useful for controller which are executed inside the DispatcherServlet. So using a Filter and expecting a @ControllerAdvice or in this case the @ExceptionHandler, to be invoked isn't going to happen.

You need to either put the same logic in the filter (for writing a JSON response) or instead of a filter use a HandlerInterceptor which does this check. The easiest way is to extend the HandlerInterceptorAdapter and just override and implement the preHandle method and put the logic from the filter into that method.

public class ClientKeyInterceptor extends HandlerInterceptorAdapter {

    @Value('${CLIENT_KEY}')
    String clientKey

    @Override
    public boolean preHandle(ServletRequest req, ServletResponse res, Object handler) {
        String reqClientKey = req.getHeader('Client-Key')
        if (!clientKey.equals(reqClientKey)) {
          throw new AccessForbiddenException('Invalid API key')
        }
        return true;
    }

}
like image 54
M. Deinum Avatar answered Oct 22 '22 22:10

M. Deinum