Java 不同请求方式的httpServer基本认证

Java httpServer basic authentication for different request methods

我在 Java 中使用一个非常简单的 httpServer 来实现 api 休息与 GET、POST、PUT 和 DELETE。我正在使用基本身份验证,我有几个 classes Authentication.java 和 Authorisation.java,我用它们来验证和检查用户的权限。

所以,问题是我希望所有用户(经过身份验证)能够从我的 api 休息中获取信息,但只有具有特定权限的用户才能 POST,PUT并删除。那我该怎么做呢?

这就是我得到的

public class Server {

  private static HttpServer server;

  public static void start() throws IOException {

    server = HttpServer.create(new InetSocketAddress(8000), 0);
    HttpContext ctx = server.createContext("/users", new UserHandler());
    ctx.setAuthenticator(new ApiRestBasicAuthentication("users"));

    server.start();
  }

}

这是我的 ApiRestBasicAuthentication

public class ApiRestBasicAuthentication extends BasicAuthenticator {

  private UserAuthentication authentication = new UserAuthentication();

  public ApiRestBasicAuthentication(String realm) {
    super(realm);
  }

  @Override
  public boolean checkCredentials(String user, String pwd) {
    int authCode = authentication.authenticate(user, pwd);
    return authCode == UserAuthentication.USER_AUTHENTICATED;
  }

}

就像现在一样,检查凭据仅检查用户是否已通过身份验证。 但我想检查一下,如果方法是 POST、DELETE 或 PUT,我还应该检查具体的凭据。但是如何在我的 ApiRestBasicAuthentication 中获取该方法?我在我的处理程序中这样做 class

public void handle(HttpExchange httpExchange) throws IOException {
    String method = httpExchange.getRequestMethod();
    if ("post".equalsIgnoreCase(method)) {
      createUser(httpExchange);
    } else if ("get".equalsIgnoreCase(method)) {
      readUsers(httpExchange);
    } else if ("put".equalsIgnoreCase(method)) {
      updateUser(httpExchange);
    } else if ("delete".equalsIgnoreCase(method)) {
      deleteUser(httpExchange);
    }
  }

也许这应该以其他方式完成。 有什么想法吗?

非常感谢。

可能有很多方法可以解决这个问题。这是我的建议之一:

  1. 创建一个用户对象,其中包含您想要的字段和一个名为 "role" 的字段。比方说只有 "admins" 可以发出除 "GET" 以外的 Http 请求,而 "regular" 用户只能发出 "GET"。有很多方法可以做到这一点,但一种方法是创建 "role" 字段 String 并使用 ENUM 为其赋值,这样以后更改起来很容易,并且只使用特定的术语。但你不必那样做。为您创建的字段和您以后可能需要的字段编写 get 和 set 方法,并且绝对是角色。

  2. 您需要确保包含句柄 (HttpExchange httpExchange) 的 class 能够看到当前登录的用户,并引用与他们关联的用户对象。然后你需要修改方法使得

if(loggedInUser.getRole().equals("admin")){
  //allow whatever method 
} else {
  // allow "GET" or give some denied permission error
}

由于没有提供其他实现,我无法给出更详细的答案或确保这对您有用。

一个简单的方法是改变你的 ApiRestBasicAuthentication 喜欢:

public class ApiRestBasicAuthentication extends BasicAuthenticator {

  private UserAuthentication authentication = new UserAuthentication();

  public ApiRestBasicAuthentication(String realm) {
    super(realm);
  }

  @Override
  public Authenticator.Result authenticate(HttpExchange exch) {
      Authenticator.Result result=super.authenticate(exch);
      if(result instanceof Authenticator.Success) {
          HttpPrincipal principal=((Authenticator.Success)result).getPrincipal();
          String requestMethod=exch.getRequestMethod();       
          if( ADD SOME LOGIC HERE FOR PRINCIPAL AND REQUEST METHOD) {
              return new return new Authenticator.Failure(401);
          }
          return result;

      }
  }

  @Override
  public boolean checkCredentials(String user, String pwd) {
    int authCode = authentication.authenticate(user, pwd);
    return authCode == UserAuthentication.USER_AUTHENTICATED;
  }

}

并在其中为 requests/users 添加一些您想让身份验证器失败的逻辑。我在这里向您展示了如何在 authenticate 方法中获取该方法,但您需要指定凭据类型。


另一种解决方案是,如果您检查 BasicAuthenticator 的源代码,您可以看到它是如何实现 authenticate 方法的,并且您可以以类似的方式创建自己的实现,而不是扩展 BasicAuthenticator 并改用 get 方法只是用户名和密码。您可以查看源代码 here,我相信您一定能找到解决方法 ;)

通常在企业应用程序中,您可以使用一些外部安全管理系统 - 例如,如果您使用 Spring(当前 java 网络应用程序中的事实标准),您可以使用 spring security 并以更具声明性的方式执行此类安全模式和过滤器

我认为您应该创建一个 AuthenticationInterceptor 并绕过 GET 那里的请求,并相应地为其余 non-GET 请求应用身份验证机制。

public class AuthenticationInterceptor extends HandlerInterceptorAdapter {

  @Autowired
  private ApiRestBasicAuthentication apiRestBasicAuthentication;

  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    switch (request.getMethod()) {
      case "GET" :
        // by-passing all GET requests
        return true;

      default :
        return apiRestBasicAuthentication.checkCredentials(username, password);
    }
  }

}

虽然上述答案可能对您有效,但我认为您还应该考虑使用定义的角色和安全约束,它们可以在您的 web.xml 中定义,并使用 @RolesAllowed 注释在 REST 资源中使用。然后,这允许您单独或在 REST resource/class 级别专门允许方法的权限。

在 web.xml 中,它看起来像这样:-

<security-role>
        <role-name>SERVERTOSERVER</role-name>
    </security-role>
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>REST API description</web-resource-name>
            <url-pattern>/<path name>/*</url-pattern>
            <http-method>GET</http-method>
        </web-resource-collection>
        <auth-constraint>
            <description>Only allow users 
        from following roles</description>
            <role-name>SERVERTOSERVER</role-name>
        </auth-constraint>
    </security-constraint>

以下链接有一些示例:Purpose of roles tags in tomcat-users.xml?https://www.thecoderscorner.com/team-blog/hosting-servers/17-setting-up-role-based-security-in-tomcat/

如果有帮助,这里是基于 Jersey 的应用程序的另一种解决方案:https://howtodoinjava.com/jersey/jersey-rest-security/