0

In my project, I added spring security using jwt. If token is missing or invalid, it will response with 401 and if permission is not set right, it will response with 403. I was wondering how I can customize the body of these two responses. I am creating response with custom messages using @ControllerAdvice like this:

@ControllerAdvice
public class HealthDataProviderRestExceptionHandler {

    private static final Logger LOG = LoggerFactory.getLogger(HealthDataProviderRestExceptionHandler.class);
    private static final String INVALID_REQUEST_MSG = "Invalid Request.";
    private static final String PROCESSING_ERROR_MSG = "Error while processing request.";

    /**
     * Customizes response for {@link HttpMessageNotReadableException}.
     *
     * @param ex the httpMessageNotReadableException
     * @return HttpStatus.BAD_REQUEST
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<Object> handleIncorrectJsonException(HttpMessageNotReadableException ex) {
        LOG.error(INVALID_REQUEST_MSG, ex);
        return new ResponseEntity<>(getErrorMessage(INVALID_REQUEST_MSG, OperationOutcome.IssueType.INVALID),
                setHeaders(), HttpStatus.BAD_REQUEST);
    }

    /**
     * Customizes response for {@link MethodArgumentNotValidException}.
     *
     * @param ex the methodArgumentNotValidException
     * @return HttpStatus.BAD_REQUEST
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        LOG.error(INVALID_REQUEST_MSG, ex);
        return new ResponseEntity<>(getErrorMessage(INVALID_REQUEST_MSG, OperationOutcome.IssueType.INVALID),
                setHeaders(), HttpStatus.BAD_REQUEST);
    }

    /**
     * Customized response for {@link HealthDataProviderServiceException}.
     *
     * @param ex the HealthDataProviderServiceException
     * @return HttpStatus.INTERNAL_SERVER_ERROR
     */
    @ExceptionHandler(HealthDataProviderServiceException.class)
    public ResponseEntity<Object> handleHealthDataProviderServiceException(
            final HealthDataProviderServiceException ex) {
        LOG.error(PROCESSING_ERROR_MSG, ex);
        return new ResponseEntity<>(getErrorMessage(PROCESSING_ERROR_MSG, OperationOutcome.IssueType.EXCEPTION),
                setHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    /**
     * Customized response for {@link HealthDataProxyException}.
     *
     * @param ex HealthDataProxyException
     * @return http status returned from proxy
     */
    @ExceptionHandler(HealthDataProxyException.class)
    public ResponseEntity<Object> handleHealthDataProxyException(final HealthDataProxyException ex) {
        LOG.error(PROCESSING_ERROR_MSG, ex);
        return new ResponseEntity<>(ex.getHealthDataProxyResponse(), setHeaders(), ex.getStatus());
    }

    /**
     * Customizes response for {@link MethodArgumentTypeMismatchException }.
     *
     * @return HttpStatus.BAD_REQUEST
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseEntity<Object> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException ex) {
        LOG.error(INVALID_REQUEST_MSG, ex);
        return new ResponseEntity<>(getErrorMessage(INVALID_REQUEST_MSG, OperationOutcome.IssueType.INVALID),
                setHeaders(), HttpStatus.BAD_REQUEST);
    }

    /**
     * Customizes response for {@link ConstraintViolationException }.
     *
     * @return HttpStatus.BAD_REQUEST
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<Object> handleConstraintViolationException(ConstraintViolationException ex) {
        LOG.error(INVALID_REQUEST_MSG, ex);
        return new ResponseEntity<>(getErrorMessage(INVALID_REQUEST_MSG, OperationOutcome.IssueType.INVALID),
                setHeaders(), HttpStatus.BAD_REQUEST);
    }

    /**
     * Customizes response for {@link Exception}.
     *
     * @param ex the exception
     * @return HttpStatus.INTERNAL_SERVER_ERROR
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Object> handleException(Exception ex) {
        LOG.error(PROCESSING_ERROR_MSG, ex);
        return new ResponseEntity<>(getErrorMessage(PROCESSING_ERROR_MSG, OperationOutcome.IssueType.EXCEPTION),
                setHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    private MultiValueMap<String, String> setHeaders() {
        MultiValueMap<String, String> headers = new HttpHeaders();
        headers.set(CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        headers.set(ACCEPT, MediaType.APPLICATION_JSON_VALUE);
        return headers;
    }

    private String getErrorMessage(String message, OperationOutcome.IssueType issueType) {
        OperationOutcome operationOutcome = new OperationOutcome();
        OperationOutcome.OperationOutcomeIssueComponent component =
                new OperationOutcome.OperationOutcomeIssueComponent();
        component.setSeverity(OperationOutcome.IssueSeverity.ERROR);
        component.setCode(issueType);
        component.setDiagnostics(message);
        operationOutcome.setId(String.valueOf(UUID.randomUUID()));
        operationOutcome.addIssue(component);
        return FhirContext.forR4().newJsonParser().encodeResourceToString(operationOutcome);
    }
}

Based on different exception type, I am generating status and messages. But I am not sure what kind of exception it is throwing for 401 and 403. I am guessing some type of NotAuthorizedException but not 100% sure.. How can I handle them with custom messages?

Jonathan Hagen
  • 580
  • 1
  • 6
  • 29
  • Does this answer your question? [Handle spring security authentication exceptions with @ExceptionHandler](https://stackoverflow.com/questions/19767267/handle-spring-security-authentication-exceptions-with-exceptionhandler) – xerx593 Mar 10 '23 at 17:26

1 Answers1

0

Look at AuthenticationFailureHandler and AccessDeniedHandler (return your implementation as @Bean).

Bill Mair
  • 1,073
  • 6
  • 15