2

I am using Spring Boot 1.5.9 for developing my application. I need implement jwt authentication, and I used jjwt library. The following code is from my custom authentication security filter which inherits from OncePerRequestFilter. Here I tried to parse the username from token, when username is parsing automatically is jwt verified and also check expiration of token. I debug it and it works, so I next want to send the correct message to the client app why authentication failed. I want to throw an ExpiredJwtException and handle it with the controller advice where I format the output.

Here is exception throwing:

try {
    username = jwtTokenService.getUsername(authToken);
} catch (IllegalArgumentException e) {
    logger.error("an error occured during getting username from token", e);
} catch (ExpiredJwtException e) {
    logger.warn("the token is expired and not valid anymore", e);
    throw new ExpiredJwtException(e.getHeader(), e.getClaims(), e.getMessage());
}

And here is my controller Advice, JwtException is base class of ExpiredJwtException which I throw so it should work. I also tried directly use ExpiredJwtException in ExceptionHandler, but didn't work as well. Next I want to handle another exceptions with same way.

@ControllerAdvice
public class GlobalControllerExceptionHandler {

    @ExceptionHandler(Exception.class)
    public @ResponseBody
    ResponseEntity<Map<String, Object>> handleException(Exception ex) {
        Map<String, Object> errorInfo = new HashMap<>();
        errorInfo.put("message", ex.getMessage());
        errorInfo.put("status", HttpStatus.BAD_REQUEST);
        errorInfo.put("status_code", HttpStatus.BAD_REQUEST.value());
        return new ResponseEntity<>(errorInfo, HttpStatus.BAD_REQUEST);
    }


    @ExceptionHandler(JwtException.class)
    //@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
    public @ResponseBody
    ResponseEntity handleJwtException(JwtException ex) {
        Map<String, Object> errorInfo = new HashMap<>();
        errorInfo.put("message", ex.getLocalizedMessage());
        errorInfo.put("status", HttpStatus.UNPROCESSABLE_ENTITY);
        errorInfo.put("status_code", HttpStatus.UNPROCESSABLE_ENTITY.value());
        return new ResponseEntity<>(errorInfo, HttpStatus.UNPROCESSABLE_ENTITY);
    }

}

Here is my folder structure: enter image description here

I want return just response with 4xx status, but I always got 5xx Internal error when my exception is thrown. Can you tell me what is wrong with my code? Thanks in advice.

IARI
  • 1,217
  • 1
  • 18
  • 35
Denis Stephanov
  • 4,563
  • 24
  • 78
  • 174

3 Answers3

5

If the exception is thrown in filter, Springs exception handling (@ControllerAdvice, @ExceptionHandler) is not involved. You need to catch all exceptions inside filter and work directly with ServletResponse.

As I understand - Filters are low level logic (request handling before spring infrastructure), but you can have a workaround, like a specific filter that wraps chaining and catches all RuntimeExceptions. (Looks like a crunch, but no other solutions).

If you want to have a specific login to create your exception object - override ErrorAttributes bean. It will allow you to have a single view for all application exceptions.

To directly specify http response status use
httpServletResponse.setStatus(... your status code ...);

Bogdan Oros
  • 1,249
  • 9
  • 13
  • Your solution not works for me otherwise you was right with filter chain and exceptions ... T found this but I don't know how to use that handler ... can you help me with this? https://stackoverflow.com/questions/30335157/make-simple-servlet-filter-work-with-controlleradvice – Denis Stephanov Feb 02 '18 at 15:01
  • Adding an interceptor to handle exceptions is also not the best idea. ` throw new ExpiredJwtException(e.getHeader(), e.getClaims(), e.getMessage());`. What is the problem to pass all this into ServletResponse directly? servletResponse.sendError(code, e.getMessage()) – Bogdan Oros Feb 02 '18 at 15:15
  • I also have another idea ... it good practice to omit security filter, and parse and check request header in controller? next I can move logic into service and handle exceptions as well. – Denis Stephanov Feb 02 '18 at 15:18
  • It is an awful idea, Denis. Please, respect SingleResponsibilty in your code. – Bogdan Oros Feb 02 '18 at 15:19
2

Have your controller extend ResponseEntityExceptionHandler and have your exception handling methods take in the WebRequest as a parameter

Then change your return value to this

return handleExceptionInternal(ex, errorInfo, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);

The HttpStatus.BAD_REQUEST can be changed to any 40x error

Example for Exception.class

@ExceptionHandler(value = { Exception.class })
protected ResponseEntity<Object> handleUncaughtException(Exception ex, WebRequest request) {

  String message = "Something bad happened";

  return handleExceptionInternal(ex, message, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}

According to this Make simple servlet filter work with @ControllerAdvice you can create a custom handler.

Then add your new handler to your WebSecurityConfigurerAdapter

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new CustomHandler());
}
locus2k
  • 2,802
  • 1
  • 14
  • 21
  • I'm sorry but doesn't work ... I still got Internal Server Error and controller advice didn't hit. – Denis Stephanov Feb 02 '18 at 14:19
  • From what I am reading `ControllerAdvice`only works for exceptions thrown from `@Controllers`. Are you throwing those exceptions inside a Controller or a Service the controller calls? – locus2k Feb 02 '18 at 14:40
  • Also please see this, it might lead to a solution as well https://stackoverflow.com/questions/30335157/make-simple-servlet-filter-work-with-controlleradvice – locus2k Feb 02 '18 at 14:41
  • It looks like what I need ... I implement custom HandlerInterceptor ... but can tell me how to add it to my configuration? I am using WebSecurityConfigurerAdapter ... is possible to add it here? – Denis Stephanov Feb 02 '18 at 14:55
  • I added how to do it in my original post – locus2k Feb 02 '18 at 15:02
  • thanks, but In my WebSecurityConfigurerAdapter is nothing method addInterceptors() to override :/ – Denis Stephanov Feb 02 '18 at 15:11
  • I tried add WebMvcConfigurerAdapter and add interceptor ... but it didnt works – Denis Stephanov Feb 02 '18 at 15:13
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/164411/discussion-between-locus2k-and-denis-stephanov). – locus2k Feb 02 '18 at 15:16
  • How did you solve it? I can't find the method to override in the class – Robs Jul 05 '22 at 14:59
0

I also faced this issue in which RestControllerAdivce was not handling the exception, Thing is that advice method can have only those arguments in its signature which exception throwing method have or can provide. My AOP method was not having access to Headers so it could not provide Headers to RestControllerAdivce method. As soon as I created a new exception handler method in RestController without Headers as argument, RestControllerAdivce started working as expected. Detials here

Vikky
  • 1,123
  • 14
  • 16