Calling a abstract method from a callback inside a abstract class

1.1k views Asked by At

I am using undertow as my HTTP library and want to verify the JWT token and HTTP method of every request. I don't want to implement the verification in every HttpHandler. Is this the right way to do it?

Handler.java

public abstract class Handler implements HttpHandler {

    private HttpString[] methods;

    Handler(HttpString... methods) {
        this.methods = methods;
    }

    @Override
    public void handleRequest(HttpServerExchange httpServerExchange) throws Exception {
        // verifying HTTP method
        boolean verified = false;
        for (HttpString method : methods) {
            if (httpServerExchange.getRequestMethod().equals(method)) {
                verified = true;
                break;
            }
        }

        if (!verified) {
            // return http 405, cause: invalid HTTP method
            httpServerExchange.setStatusCode(StatusCodes.METHOD_NOT_ALLOWED);
            httpServerExchange.getResponseSender().send(Variables.Response.EMPTY);
        }

    // verifying JWT token
    String jwt = httpServerExchange.getRequestHeaders().get("jwt", 0);
    JWT.verifyToken(jwt)
            .addListener(token -> {
                if (token != null) {
                    handleVerifiedRequest(httpServerExchange, token);
                } else {
                    // return http 400, cause: JWT invalid
                    httpServerExchange.setStatusCode(StatusCodes.UNAUTHORIZED);
                    httpServerExchange.getResponseSender().send(Variables.Errors.INVALID_JWT);
                }
            });
    }

    public abstract void handleVerifiedRequest(HttpServerExchange httpServerExchange, String Token);
}

HelloHandler.java

public class HelloHandler extends Handler {

    public HelloHandler(HttpString... methods) {
        super(methods);
    }

    @Override
    public void handleVerifiedRequest(HttpServerExchange httpServerExchange, String Token) {
        // .. do something
    }
}
2

There are 2 answers

0
ant1g On BEST ANSWER

A more reusable and recommended approach would be to stick with the Undertow HttpHandler API and to chain handlers together.

First, as you already proposed, your JWT authentication handler, checking for an incoming token in the request:

public class JwtAuthHandler implements HttpHandler {

  AttachmentKey<JwtToken> JWT_TOKEN = AttachmentKey.create(JwtToken.class);

  private final HttpHandler next;
  public JwtAuthHandler(HttpHandler next) {
    this.next = next;
  }

  @Override
  public void handleRequest(HttpServerExchange exchange) throws Exception {
    ...
    JWT.verifyToken(jwt)
        .addListener(token -> {
          if (token != null) {
            exchange.putAttachment(JWT_TOKEN, token);
            next.handleRequest(exchange);
          } else {
            // return http 400, cause: JWT invalid
            httpServerExchange.setStatusCode(StatusCodes.UNAUTHORIZED);
            httpServerExchange.getResponseSender().send(Variables.Errors.INVALID_JWT);
          }
        });
  }
}

One difference though, it simply implements the HttpHandler interface and expects a next HttpHandler to call in case of success. Before calling the handleRequest method on the next handler, note the line to add the current valid token as an exchange attachment. Attachments are a way to pass data between handlers.

Then, your HelloHandler simply expects a JwtToken from the exchange attachments (note that this is just a guess, I don't know what JWT library do you use, this is simply the type of the token variable in your example.)

public class HelloHandler implements HttpHandler {

  @Override
  public void handleRequest(HttpServerExchange exchange) throws Exception {
    JwtToken token = exchange.getAttachment(JWT_TOKEN);
    ...
  }
}

This handler will only be called if the request authentication is successful.

As you probably know, handlers are meant to be chained together:

Undertow.builder()
    .addHttpListener(8080, "0.0.0.0")
    .setHandler(new JwtAuthHandler(new HelloHandler()))
    .build().start();

If you stick with the HttpHandler API, you can easily integrate and use existing handlers that Undertow provides, look here.

0
Atreys On

Your approach will force a subclass to implement handleVerifiedRequest but will also allow someone to reimplement handleRequest to circumvent your verification. To prevent a subclass from doing so, add the final keyword to the original method in your abstract class.

public abstract class Handler implements HttpHandler {
    // ... //

    @Override
    public final void handleRequest(HttpServerExchange httpServerExchange) throws Exception {
       // ... your verification code ... //
    }

    public abstract void handleVerifiedRequest(HttpServerExchange httpServerExchange, String Token);
}