Java 11 HttpClient未发送基本身份验证

23

我写了下面的HttpClient代码,但是它没有发送Authorization头到服务器:

public static void main(String[] args) {
    var client = HttpClient.newBuilder()
            .authenticator(new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication("username", "password".toCharArray());
                }
            })
            .version(HttpClient.Version.HTTP_1_1)
            .build();
    var request = HttpRequest.newBuilder()
            .uri("https://service-that-needs-auth.example/")
            .build();
    client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
            .thenApply(HttpResponse::body)
            .thenAccept(System.out::println)
            .join();
}

我从调用的服务中得到了一个HTTP 401错误,在我的情况下,这是Atlassian Jira Cloud API。

我确认HttpClient没有调用我的getPasswordAuthentication()方法。

为什么它不起作用,我应该做些什么?

1个回答

52

我要调用的服务(在本例中是Atlassian的Jira Cloud API)支持基本身份验证和OAuth身份验证。我试图使用HTTP Basic,但它会返回一个OAuth的认证挑战。

在当前的JDK 11版本中,HttpClient不会发送Basic凭据,直到从服务器收到WWW-Authenticate头部的挑战为止。此外,它只能理解基本身份验证类型的挑战。如果你想查看,相关的JDK代码在这里(包括支持多种身份验证的TODO)。

与此同时,我的解决方法是绕过HttpClient的身份验证API并自己创建和发送Basic Authorization头部。

public static void main(String[] args) {
    var client = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_1_1)
            .build();
    var request = HttpRequest.newBuilder()
            .uri(new URI("https://service-that-needs-auth.example/"))
            .header("Authorization", basicAuth("username", "password"))
            .build();
    client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
            .thenApply(HttpResponse::body)
            .thenAccept(System.out::println)
            .join();
}

private static String basicAuth(String username, String password) {
    return "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
}

8
谢谢您报告这个问题 - 现在已记录为https://bugs.openjdk.java.net/browse/JDK-8217237。您建议的解决方法似乎是正确的。或者,如果您不提供任何Authenticator,并希望先检查服务器挑战,您可以选择处理401/407回复,而不是预先提供Authorization标头。 - daniel
感谢您提交该问题。这将对HttpClient的标准合规性产生重要的改进。但我想澄清一下:此处的问题与您在JDK-8217237中描述的问题不同。在Atlassian Cloud的情况下,服务器发送的唯一身份验证挑战是OAuth。没有基本的身份验证挑战。尝试curl -v https://example.atlassian.net/rest/agile/1.0/board/44/issue以查看响应头,在这种情况下HttpClient将处理这些响应头。 - Jonathan Fuerth
哦,如果服务器没有发送基本的挑战,那么 HttpClient 就无能为力了:HttpClient 只实现了基本的开箱即用功能。但是这个 bug (JDK-8217237) 仍然是一个 bug。 - daniel
同意。再次感谢! - Jonathan Fuerth
openjdk版本“1.8.0_191”从代理接收407,只有通过设置HttpURLConnection.setRequestProperty("Proxy-Authorization", basicAuth)才能正常工作。jdk.http.auth.tunneling.disabledSchemes=""无法解决问题。 - weberjn
1
相当疯狂!我简直不敢相信这个修复版本是13 :| 今天我们遇到了这个问题,我一直对开发人员说显然他做错了什么,结果我错得离谱... - Eugene

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