简体   繁体   中英

Getting error details from JHipster UAA microservice through gateway

Context: our app uses a JHipster generated gateway and UAA service (microservice architecture). We're implementing blocking a user account after too many failed attempts.

The UAA service returns a 400 Bad Request when a blocked user tries to login, with a custom i18n code in the error_description field that can be used by the frontend to display the correct user error :

{
  "error" : "invalid_grant",
  "error_description" : "error.login.locked"
}

So far so good.

The issue happens when the login request is made through the gateway , because the answer is:

{
  "type" : "http://www.jhipster.tech/problem/problem-with-message",
  "title" : "Internal Server Error",
  "status" : 500,
  "detail" : "400 Bad Request",
  "path" : "/auth/login",
  "message" : "error.http.500"
}

There are 2 issues here :

  1. The HTTP status is 500. It's also the case when login fails because of a wrong password... Is that the normal, expected behavior for the generated gateway ?

  2. The error and error_description fields that are necessary for the frontend were lost passing through the gateway.

Is there a better way than to edit the gateway authenticate method, checking the HttpClientErrorExceptionfor and parsing an OAuth2Exception to get the details, raising a custom exception which is then handled by the Exceptiontranslator in order to conserve the needed fields? That seems a bit much, just to be able to preserve data sent by the service.

Here's what I ended up using - might not be the proper, optimal solution but it worked as I needed:

In the OAuth2AuthenticationService , I catch the exception (so that it doesn't bubble up as a 500 Internal Error)

 public ResponseEntity<OAuth2AccessToken> authenticate(HttpServletRequest request, HttpServletResponse response,
                                                      Map<String, String> params) {
    try {
        [...]
        OAuth2AccessToken accessToken = authorizationClient.sendPasswordGrant(username, password);
        [...]
        return ResponseEntity.ok(accessToken);
    } catch (HttpClientErrorException ex) {
        ObjectMapper mapper = new ObjectMapper();
        try {
            // Throw the original exception from auth-service to keep its message (i18n code used by the frontend in my case)
            throw mapper.readValue(ex.getResponseBodyAsString(), OAuth2Exception.class);
        } catch (IOException e) {
            // If it's not a OAuth2Exception, let the error get thrown
            throw ex;
        }
    } catch (Exception ex) {
        // If it's not a OAuth2Exception, let the error get thrown
        throw ex;
    }

In the AuthResource.authenticate() method, I catch these OAuth2Exception, wrap them in a custom AuthenticationFailureException class and let the ExceptionTranslator return the right object to the frontend (note the BadRequest Status and the message payload):

@ExceptionHandler(AuthenticationFailureException.class)
public ResponseEntity<Problem> handleBadRequestAlertException(AuthenticationFailureException ex, NativeWebRequest request) {
    Problem problem = Problem.builder()
        .withStatus(BAD_REQUEST)
        .with("message", ex.getMessage())
        .build();
    return create(ex, problem, request);
}

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