Home > OS >  custom exception is not passing through Advice control in spring boot
custom exception is not passing through Advice control in spring boot

Time:10-01

I'm trying to customize a thrown exception, but it's not in the advice control.

The TokenExpiredException exception should be handled in controller advice, but returns a common, unhandled error.

JWTValidarFilter:

public class JWTValidarFilter extends BasicAuthenticationFilter{

private static final String HEADER_ATRIBUTO = "Authorization";

private static final String ATRIBUTO_PREFIXO = "Bearer ";

public JWTValidarFilter(AuthenticationManager authenticationManager) {
    super(authenticationManager);
}

@Override
protected void doFilterInternal(HttpServletRequest request, 
        HttpServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    
    String atributo = request.getHeader(HEADER_ATRIBUTO);
            
    if(atributo == null) {
        chain.doFilter(request, response);
        return;
    }
    
    if(!atributo.startsWith(ATRIBUTO_PREFIXO)) {
        chain.doFilter(request, response);
        return;
    }
    
    String token = atributo.replace(ATRIBUTO_PREFIXO, "");
    
    UsernamePasswordAuthenticationToken authenticationToken = getAuthenticationToken(token);
    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
    chain.doFilter(request, response);
    
    
}

private UsernamePasswordAuthenticationToken getAuthenticationToken(String token) {
    
    
    try {
        String usuario = JWT.require(Algorithm.HMAC512(JWTAutenticarFilter.TOKEN_SENHA))
                .build()
                .verify(token)
                .getSubject();
        if(usuario == null) {
            return null;
        }
        
        return new UsernamePasswordAuthenticationToken(usuario, null, new ArrayList<>());

    } catch (TokenExpiredException e) {
        throw new TokenExpiredException("Token expirado!");
    }
    
}

}

CustomizeResponseEntityExceptionHandler:

@ControllerAdvice
@RestController
public class CustomizeResponseEntityExceptionHandler extends ResponseEntityExceptionHandler{

    @ExceptionHandler(Exception.class)
    public final ResponseEntity<ExceptionResponse> handleAllExcepetions(Exception ex, WebRequest request){
        ExceptionResponse exceptionResponse = 
                new ExceptionResponse(new Date(), ex.getMessage(), request.getDescription(false));
        
        return new ResponseEntity<>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(InvalidJwtAuthenticationException.class)
    public final ResponseEntity<ExceptionResponse> invalidJwtAuthenticationException(Exception ex, WebRequest request){
        ExceptionResponse exceptionResponse = 
                new ExceptionResponse(new Date(), 
                        ex.getMessage(), 
                        request.getDescription(false));
        
        return new ResponseEntity<>(exceptionResponse, HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(TokenExpiredException.class)
    public final ResponseEntity<ExceptionResponse> TokenExpiredException(TokenExpiredException ex, WebRequest request){
        ExceptionResponse exceptionResponse = 
                new ExceptionResponse(new Date(), 
                        ex.getMessage(), 
                        request.getDescription(true));
        
        return new ResponseEntity<>(exceptionResponse, HttpStatus.UNAUTHORIZED);
    }

    
    @ExceptionHandler(HttpClientErrorException.class)
    public ResponseEntity<String> handleException(HttpClientErrorException ex) throws HttpClientErrorException {
        System.out.println("*******Exception Occured: *************"   ex);
        return ResponseEntity
                .status(HttpStatus.UNAUTHORIZED)
                .body(" -----DD------ Exception: "   ex.getLocalizedMessage());
    }
}

2021-09-28 10:13:38.729 ERROR 25385 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception com.auth0.jwt.exceptions.TokenExpiredException: Token expirado!

CodePudding user response:

Try building a custom AuthenticationFailureHandler as follows:

public class CustomAuthenticationFailureHandler 
  implements AuthenticationFailureHandler {
 
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
       AuthenticationException exception) throws IOException, ServletException {

        Throwable cause = exception.getCause();
        ExceptionResponse exceptionResponse = null;

        if (cause instanceOf InvalidJwtAuthenticationException) {
          response.setStatus(HttpStatus.BAD_REQUEST.value());
          exceptionResponse = new ExceptionResponse(new Date(), 
                cause.getMessage(), 
                request.getDescription(false));
        } else if (cause instanceOf TokenExpiredException) {
          response.setStatus(HttpStatus.UNAUTHORIZED.value());
          exceptionResponse = new ExceptionResponse(new Date(), 
                ex.getMessage(), 
                request.getDescription(true));
        } else {
          // additional logic here
        }

        response.getOutputStream().println(objectMapper.writeValueAsString(exceptionResponse));
    }
}

Then you need to register it in a @Configuration class:

@Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
    return new CustomAuthenticationFailureHandler();
}

CodePudding user response:

You can't catch authentication exceptions using controller advice (at least not as simple :-). Authentication exceptions happen before the whole Spring exception handler intialized. Here ist similar discussion how to workaround related issues:

Spring MVC (or Spring Boot). Custom JSON response for security related exceptions like 401 Unauthorized or 403 Forbidden)

  • Related