在抽象类内部的回调中调用抽象方法

3
我将使用undertow作为我的HTTP库,并想要验证每个请求的JWT令牌和HTTP方法。我不想在每个HttpHandler中实现验证。这样做是否正确? 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
    }
}

你的解决方案看起来没有任何问题。 - aiguy
@aiguy 酷,谢谢!这是我第一次实现抽象类.. :D - tomwassing
使用这种方法将防止您链接其他处理程序。我建议您坚持使用Undertow API(HttpHandler和exchange Attachments)。如果您有兴趣,我可以在实际答案中详细说明。 - ant1g
@aramaki 我非常感兴趣,非常感谢。 - tomwassing
2个回答

3

一种更可重用且推荐的方法是遵循Undertow HttpHandler API并将处理程序链接在一起。

首先,如您所提议,您的JWT身份验证处理程序应检查请求中是否存在令牌:

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);
          }
        });
  }
}

其中一个区别在于,它只是实现了HttpHandler接口并期望有一个next HttpHandler在成功的情况下调用。在调用下一个处理程序的handleRequest方法之前,请注意添加当前有效令牌作为交换附件的行。附件是在处理程序之间传递数据的一种方式。

然后,您的HelloHandler只需从交换附件中获取JwtToken(请注意,这只是猜测,我不知道您使用的JWT库是什么,这只是您示例中token变量的类型。)

public class HelloHandler implements HttpHandler {

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

如果请求认证成功,将调用此处理程序。

正如您可能知道的那样,处理程序应该链接在一起:

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

如果您坚持使用HttpHandler API,则可以轻松集成和使用Undertow提供的现有处理程序,可以在这里查看:此处

1
你的方法会强制子类实现 handleVerifiedRequest,但也允许有人重新实现 handleRequest 来规避验证。为了防止子类这样做,需要在抽象类中原始方法上添加 final 关键字。
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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接