4

I am using JWT in header for verifying user request.

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    String token = exchange.getRequest().getHeaders().getFirst("token");
    // Verify a Token
    try {
        Algorithm algorithm = Algorithm.HMAC256("secret");
        JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("auth0")
                .build(); //Reusable verifier instance
        DecodedJWT jwt = verifier.verify(token);
    } catch (UnsupportedEncodingException exception) {
        // send internal server error in response
    } catch (JWTVerificationException exception) {
        // send invalid token
    }
    return chain.filter(exchange);
}

When I use

return Mono.empty();

It ends requests, but how to set a proper response? e.g. "Invalid token" OR "Internal Server Error" in response.

Roshan Gade
  • 320
  • 3
  • 16

3 Answers3

4

In the catch block you can do the following or rethrow the exception so you have some GlobalExceptionHandler by implementing "org.springframework.web.server.WebExceptionHandler" and there you can have this logic as well. The Key here is using DataBufferFactory and then using ObjectMapper of Jackson to serialize your custom error object to string and then write the response as well as set the HTTP Status using exchange.getResponse().setStatusCode

import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@Autowired
    ObjectMapper objMapper;

        ApiError apiError = new ApiError(HttpStatus.UNAUTHORIZED);
        apiError.setTimestamp(LocalDateTime.now());
        apiError.setMessage("Invalid token" + exception.getMessage() + ":"+exception.getLocalizedMessage());
        DataBuffer buf = null;
        try {
            buf = dataBufferFactory.wrap(objMapper.writeValueAsBytes(apiError));
        } catch (JsonProcessingException e) {
            LOG.debug("Exception during processing JSON", e);
            apiError.setMessage(e.getMessage());
        }
        if(buf == null) buf = dataBufferFactory.wrap("".getBytes());
        exchange.getResponse().setStatusCode(apiError.getStatus());
        return exchange.getResponse().writeWith(Flux.just(buf));

ApiError is a custom class that has HTTP Status and timestamp and stuff like below or your custom datastructure as well.

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;

import org.springframework.http.HttpStatus;

import com.fasterxml.jackson.annotation.JsonFormat;

import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ApiError  implements Serializable{

       private HttpStatus status;
       @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "MM/dd/yyyy hh:mm:ss a")
       private LocalDateTime timestamp;
       private String message;
       private String debugMessage;


       public ApiError() {
           timestamp = LocalDateTime.now();
       }

       public ApiError(HttpStatus status) {
           this();
           this.status = status;
       }

       public ApiError(HttpStatus status, Throwable ex) {
           this();
           this.status = status;
           this.message = "Unexpected error";
           this.debugMessage = ex.getLocalizedMessage();
       }

       public ApiError(HttpStatus status, String message, Throwable ex) {
           this();
           this.status = status;
           this.message = message;
           this.debugMessage = ex.getLocalizedMessage();
       }





}
ROCKY
  • 1,763
  • 1
  • 21
  • 25
2

Maybe this will help, this is for x509 authentication but it will work for JWT.

Check Authentication by certificate for WebFlux?

Key points are:

  1. Use the authentication converter to extract credentials (the authentication filter will take care of calling the ReactiveAuthenticationManager to authenticate the extracted credentials)
  2. Use AuthenticationEntryEndpoint, if needed, to customize the response back to client in case of authentication failure

Hope this helps

bsamartins
  • 141
  • 4
1

You can modify headers of Response:

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
    String token = exchange.getRequest().getHeaders().getFirst("token");
    // Verify a Token
    try {
        Algorithm algorithm = Algorithm.HMAC256("secret");
        JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("auth0")
                .build(); //Reusable verifier instance
        DecodedJWT jwt = verifier.verify(token);
    } catch (UnsupportedEncodingException exception) {
        // send internal server error in response
        exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        exchange.getResponse().getHeaders().add("X-Message", "Unsupported Encoding");
        return Mono.empty();
    } catch (JWTVerificationException exception) {
        // send invalid token
        exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
        exchange.getResponse().getHeaders().add("X-Message", "Invalid token");
        return Mono.empty();
    }
    return chain.filter(exchange);
}

I couldn't find a way to write the body of Response

Andrielson
  • 11
  • 2