Reputation: 5231
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);
}
}
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.
Upvotes: 3
Views: 23190
Reputation: 1223
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
Upvotes: 0
Reputation: 2935
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());
}
Upvotes: 2
Reputation: 1299
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 usehttpServletResponse.setStatus(... your status code ...)
;
Upvotes: 6