简体   繁体   中英

Java Spring Boot Custom Exception Handler thrown out of controller

I'm trying to throw my custom exception in the "unsuccessfulAuthentication", the problem is I can't catch it in my global exception handler. It's like exceptions thrown out of controllers and classes which are called by controllers (services) are not getting caught in global exception handler.

My Authentication Filter

package com.ekahau.booking.security;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.ekahau.booking.exception.UnAuthorizedException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;

@Slf4j
public class CustomAuthFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authManager;

    public CustomAuthFilter(AuthenticationManager authManager) {
        this.authManager = authManager;
    }

    @Override
    protected void unsuccessfulAuthentication(
            HttpServletRequest request,
            HttpServletResponse response,
            AuthenticationException failed
    ) throws IOException, ServletException {
        response.setContentType(APPLICATION_JSON_VALUE);

        System.out.println("------------------------------");
        System.out.println("------------------------------");
        System.out.println("------------------------------");
        System.out.println("------------------------------");

        throw new UnAuthorizedException("Invalid Credentials");
//        super.unsuccessfulAuthentication();
//        Map<String, String> errorResponse = new HashMap<>();
//        errorResponse.put("timestamp", (new Date().toString()));
//        errorResponse.put("status", HttpStatus.UNAUTHORIZED.toString());
//        errorResponse.put("message", "Invalid credentials");

        //Add more descriptive message
//        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed");
//        new ObjectMapper().writeValue(response.getOutputStream(), errorResponse);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        log.info("Username: {} with password: {} attempted to login", username, password);
        UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                username,
                password
        );
        return authManager.authenticate(authToken);
    }

    @Override
    protected void successfulAuthentication(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain chain,
            Authentication authentication
    ) throws IOException, ServletException {
        var user = (UserDetails) authentication.getPrincipal();
        var algo = Algorithm.HMAC256("secret".getBytes());
        var claim = user.
                getAuthorities().
                stream().
                map(GrantedAuthority::getAuthority).collect(Collectors.toList());
        var accessToken = JWT.create()
                .withSubject(user.getUsername())
                .withExpiresAt(new Date(System.currentTimeMillis() + 2 * 60 * 60 * 1000 ))
                .withIssuer(request.getRequestURL().toString())
                .withClaim("roles", claim)
                .sign(algo);

        var refreshToken = JWT.create()
                .withSubject(user.getUsername())
                .withExpiresAt(new Date(System.currentTimeMillis() + 12 * 60 * 60 * 1000 ))
                .withIssuer(request.getRequestURL().toString())
                .withClaim("roles", claim)
                .sign(algo);

        // return token in the response body
        response.setContentType(APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");

        /* old fashion way
        response.getWriter().write(String.format(
                "{\"access_token\":\"%s\", \"refresh_token\":\"%s\"", accessToken, refreshToken
        ));*/
        Map<String, String> tokens = new HashMap<>();
        tokens.put("access_token", accessToken);
        tokens.put("refresh_token", refreshToken);

        new ObjectMapper().writeValue(response.getOutputStream(), tokens);
        /*
        return token in the headers
        response.set("access_token", accessToken);
        response.setHeader("refresh_token", refreshToken);
        */
    }

}

My global exception handler:

    /***
     * @param ex the ex
     * @param request the request
     * @return the response entity
     */
    @ExceptionHandler(UnAuthorizedException.class)
    public ResponseEntity<?> handleUnAuthorizedException(
            UnAuthorizedException ex, WebRequest request) {
        
        ErrorResponse errorDetails =
                new ErrorResponse(
                        new Date(),
                        HttpStatus.UNAUTHORIZED.toString(),
                        ex.getMessage(),
                        request.getDescription(false),
                        null
                );
        return new ResponseEntity<>(errorDetails, HttpStatus.UNAUTHORIZED);
    }

and my complete global exception handler:

package com.ekahau.booking.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@ControllerAdvice
@RestControllerAdvice
public class GlobalExceptionHandler {

    /***
     * @param ex the ex
     * @param request the request
     * @return the response entity
     */
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<?> handleResourceNotFoundException(
            ResourceNotFoundException ex, WebRequest request) {
        ErrorResponse errorDetails =
                new ErrorResponse(
                        new Date(),
                        HttpStatus.NOT_FOUND.toString(),
                        ex.getMessage(),
                        request.getDescription(false),
                        null
                );
        return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
    }


    /***
     * @param ex the ex
     * @param request the request
     * @return the response entity
     */
    @ExceptionHandler(UnAuthorizedException.class)
    public ResponseEntity<?> handleUnAuthorizedException(
            UnAuthorizedException ex, WebRequest request) {

        ErrorResponse errorDetails =
                new ErrorResponse(
                        new Date(),
                        HttpStatus.UNAUTHORIZED.toString(),
                        ex.getMessage(),
                        request.getDescription(false),
                        null
                );
        return new ResponseEntity<>(errorDetails, HttpStatus.UNAUTHORIZED);
    }


    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<?> handleConstraintViolationException(
            ConstraintViolationException ex,
            WebRequest request) {

        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();

        var errors = new HashMap<String, Set<String>>();

        constraintViolations.forEach(violation -> {
            var messages = new HashSet<String>();
            messages.add(String.format(
                    "%s value '%s' %s",
                    violation.getPropertyPath(),
                    violation.getInvalidValue(),
                    violation.getMessage()));

            errors.put(violation.getPropertyPath().toString(), messages);
        });

        ErrorResponse errorDetails =
                new ErrorResponse(
                        new Date(),
                        HttpStatus.NOT_FOUND.toString(),
                        "Validation Exception",
                        request.getDescription(false),
                        errors
                );
        return new ResponseEntity<>(errorDetails, HttpStatus.UNPROCESSABLE_ENTITY);
    }

    /**
     * Validation exception handler
     *
     * @param ex the ex
     * @param request the request
     * @return the response entity
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    protected ResponseEntity<Object> handleMethodArgumentNotValid(
            MethodArgumentNotValidException ex,
            WebRequest request
    ) {
        final List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
        Map<String, Set<String>> errorsMap = fieldErrors.stream().collect(
                Collectors.groupingBy(FieldError::getField,
                        Collectors.mapping(FieldError::getDefaultMessage, Collectors.toSet())
                )
        );
        ErrorResponse errorDetails =
                new ErrorResponse(
                        new Date(),
                        HttpStatus.UNPROCESSABLE_ENTITY.toString(),
                        ex.getMessage() + " exception class : " + ex.getClass(),
                        request.getDescription(false),
                        (HashMap<String, Set<String>>) errorsMap
                );
        return new ResponseEntity<>(errorDetails, HttpStatus.UNPROCESSABLE_ENTITY);
    }


    /**
     * Globle excpetion handler response entity.
     *
     * @param ex the ex
     * @param request the request
     * @return the response entity
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> handleGlobleExcpetionHandler(Exception ex, WebRequest request) {
        ErrorResponse errorDetails =
                new ErrorResponse(
                        new Date(),
                        HttpStatus.INTERNAL_SERVER_ERROR.toString(),
                        ex.getMessage() + " exception class : " + ex.getClass(),
                        request.getDescription(false),
                        null
                );
        return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

You should find the question/answer below useful in solving your query:

Make simple servlet filter work with @ControllerAdvice

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM