java – Response Customization 403 Spring Security

Question:

Hey guys!

Here is my situation:

I have an application with JWT authentication. Authentication is done in the database and I now need to add 2 more validations: login failure count and registered device. To do this, I created a CustomDaoAuthenticationProvider class extending from the DaoAuthenticationProvider (this second provided in Spring).

Inside this class, I overwritten the additionalAuthenticationChecks method and wrote the appropriate validations, each throwing a different exception (LockedException and BadCredentialsException – both children of AuthenticationException)

What I need:

The return of the request comes with the respective validation message.

What is happening:

In case of failure, the return always comes as follows:

{
    "timestamp": 1548779620976,
    "status": 403,
    "error": "Forbidden",
    "message": "Access Denied",
    "path": "/app/api/v1.1/login"
}

What have I tried to do to solve the case:

  • In JWTLoginFilter I overridden the unSuccessfulAuthentication method and made a response.sendError(). Result: The method is even called, but the default message keeps returning.
  • In JWAuthenticationFilter I put a try/catch block around the authenticate call in order to catch and throw the exception, but that doesn't happen since apparently the exception handling happens before.
  • I created a CustomAuthenticationEntryPoint following the suggestion at: https://stackoverflow.com/questions/48306302/spring-security-creating-403-access-denied-custom-response . The message actually changed, but some other exception overlapped mine and the return was:

    { "message": "Full authentication is required to access this resource", "timestamp": 1548780759071, "status": 403 }

In the latter case, I set the message in my exception to the value of the message. Does anyone have any idea how I can solve this case? Thank you in advance for your attention.

Answer:

You could create a handler and handle your specific exception for these occasions, for example:

@ControllerAdvice
public class AppExceptionHandler {

    @ExceptionHandler(value = {UserServiceException.class})
    public ResponseEntity<Object> handleUserServiceException(UserServiceException ex, WebRequest request) {
        return new ResponseEntity<Object>(new ErrorMessage(new Date(), ex.getMessage()), new HttpHeaders(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(value = {Exception.class})
    public ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
        return new ResponseEntity<Object>(new ErrorMessage(new Date(), ex.getMessage()), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
    }

}

In my case I'm returning the following entity:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ErrorMessage {

    private Date timestamp;
    private String message;

}

When I throw a UserServiceException , I get the following return:

{
    "timestamp": "2019-02-07T14:17:55.639+0000",
    "message": "Record with provided id is not found"
}

Of course you need other information in the return, but you can supplement your ErrorMessage accordingly, just as you can create a new exception related to your authentication flow and handle it in the handler.

Scroll to Top