Java HTTPServer 身份验证 - 客户端

Java HTTPServer Authentication - Client Side

我按照 here 所述实现了一个非常简单的 HTTP 服务器。我设法通过使用 Authentication header 使身份验证工作,但我无法弄清楚如何从表单中获取凭据并使用它们对服务器进行身份验证。这通常是如何完成的?

代码:

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;

import com.sun.net.httpserver.BasicAuthenticator;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class SimpleHttpServer3 {

  public static void main(String[] args) throws Exception {
    HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
    server.createContext("/info", new InfoHandler());
    HttpContext hc1 = server.createContext("/get", new GetHandler());
    hc1.setAuthenticator(new BasicAuthenticator("get") {
        @Override
        public boolean checkCredentials(String user, String pwd) {
            return user.equals("admin") && pwd.equals("password");
        }
    });
    server.setExecutor(null); // creates a default executor
    server.start();
    System.out.println("The server is running");
  }

  // http://localhost:8000/info
  static class InfoHandler implements HttpHandler {
    public void handle(HttpExchange httpExchange) throws IOException {
      String response = "Use /get to authenticate (user:admin pwd:password)";
      SimpleHttpServer3.writeResponse(httpExchange, response.toString());
    }
  }

  static class GetHandler implements HttpHandler {
    public void handle(HttpExchange httpExchange) throws IOException {
      StringBuilder response = new StringBuilder();
      response.append("<html><body>");
      response.append("hello " + httpExchange.getPrincipal().getUsername());
      response.append("</body></html>");
      SimpleHttpServer3.writeResponse(httpExchange, response.toString());
    }
  }

  public static void writeResponse(HttpExchange httpExchange, String response) throws IOException {
    httpExchange.sendResponseHeaders(200, response.length());
    OutputStream os = httpExchange.getResponseBody();
    os.write(response.getBytes());
    os.close();
  }

}

通常大多数人会使用像 Shiro 或 Spring Security 这样的身份验证框架。

这些框架在模式上注册一个 Servlet 过滤器,例如 /* 以对所有请求强制执行身份验证。它们将身份验证数据存储在 Servlet 会话中以保持用户在请求之间登录,这通常由 HTTP 服务器通过 Cookie 隐式完成。他们还注册了一个特殊的上下文来接受基于表单的身份验证请求,例如 POST 对 /login.

的请求

这些基于表单的端点将读取,通常是 application/x-www-form-urlencoded,请求并提取提交的用户名和密码,以与服务器存储密码相同的方式散列密码,并将它们进行比较以验证身份验证主体.

Basic Authentication 协议规定客户端请求应具有

形式的 header
Authorization: Basic Base64Encoded(username:password)

其中 Base64Encoded(username:password)username:password 的实际 Base64 编码字符串。例如,如果我的用户名和密码是 peeskillet:pass,则 header 应该发送为

Authorization: Basic cGVlc2tpbGxldDpwYXNz

the example from your link 中,它使用基本身份验证。受保护资源的 URL 是

http://localhost:8000/get

您可以转到 URL 的浏览器,您将看到一个对话框(在 Firefox 中),如 this answer 中所示。输入 admin(用户名)和 password(密码)。您将收到 hello admin return 消息。

你不需要自己做任何认证(或至少parsing/decoding),因为在内部(我没有检查源代码,但似乎是这样)BasicAuthenticator 解码并解析 Authorization header 并将用户名和密码传递给您实现的 checkCredentials 方法。因为它只允许 admin/password,所以这是唯一可以验证的组合。

为了完整起见,decoding/parsing(在 pseudo-code 中)可能类似于

String authHeader = request.getHeader("Authorization");
String[] split = authHeader.split("\s");
String credentials = split[1];
String decoded = Base64.decode(credentials);
String[] userPass = decoded.split(":");

String username = userPass[0];
String password = userPass[1];

boolean authenticated = checkCredentials(username, password);

如果您想尝试使用代码发出请求,则需要在请求中设置 Authorization header。该值将是 admin:password 的 Base64 编码字符串,即 YWRtaW46cGFzc3dvcmQ=。所以 header 应该作为

发送出去
Authorization: Basic YWRtaW46cGFzc3dvcmQ=

对于其他组合,您可以 go here to type in the combinations, or Java 8 has the java.util.Base64 class 用于对凭据进行编码

String cred = "admin:password";
String encoded = Base64.getEncoder().encodeToString(cred.getBytes());
System.out.println(encoded);
// YWRtaW46cGFzc3dvcmQ=