使用凭据提供程序时,Apache HttpClient 不发送身份验证标头

Sun*_*Sun 6 java apache-httpclient-4.x

编辑:我已经尝试过评论中提供的解决方案,但它们不起作用。添加原始标头是可行的,但我试图避免这样做。

我面临着一个奇怪的情况。我有一个受基本身份验证保护的资源,我可以通过浏览器、Postman 和curl 轻松查询该资源。但在 Java 中,我无法查询它并收到 403 禁止错误。我按照下面的例子:

CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("username", "password");
provider.setCredentials(AuthScope.ANY, credentials);
CloseableHttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(provider).build();

HttpResponse response = client.execute(new HttpGet("https://hostname.com/api/detail?query=xx"));
int statusCode = response.getStatusLine().getStatusCode();
System.out.println(statusCode);     // prints 403 forbidden
Run Code Online (Sandbox Code Playgroud)

当我检查调试器中的对象时,我注意到 HttpGet 对象的标头为空。也就是说,它没有传递带有“Basic asdflkhjWskjhakljhasdfkasdflkjh=”值的“Authorization”标头。

因此,我手动添加了具有正确值的标头并且它起作用了(状态 200 OK)。

有人可以帮我弄清楚为什么会发生这种情况以及如何使用 HttpClient API 设置正确的标头而不是手动添加标头?

这是我的依赖:

<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
Run Code Online (Sandbox Code Playgroud)

Jon*_*son 1

以下是通过运行代码生成的调试日志,并向虚拟机添加以下参数:

-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
-Dorg.apache.commons.logging.simplelog.showdatetime=true
-Dorg.apache.commons.logging.simplelog.log.org.apache.http=DEBUG
-Dorg.apache.commons.logging.simplelog.log.org.apache.http.wire=ERROR
Run Code Online (Sandbox Code Playgroud)

日志:

RequestAddCookies - CookieSpec selected: default
RequestAuthCache - Auth cache not set in the context
PoolingHttpClientConnectionManager - Connection request: [route: {}->http://localhost:80][total kept alive: 0; route allocated: 0 of 2; total allocated: 0 of 20]
PoolingHttpClientConnectionManager - Connection leased: [id: 0][route: {}->http://localhost:80][total kept alive: 0; route allocated: 1 of 2; total allocated: 1 of 20]
MainClientExec - Opening connection {}->http://localhost:80
DefaultHttpClientConnectionOperator - Connecting to localhost/127.0.0.1:80
DefaultHttpClientConnectionOperator - Connection established 127.0.0.1:37638<->127.0.0.1:80
MainClientExec - Executing request GET /test/ HTTP/1.1
MainClientExec - Target auth state: UNCHALLENGED
MainClientExec - Proxy auth state: UNCHALLENGED
headers - http-outgoing-0 >> GET /test/ HTTP/1.1
headers - http-outgoing-0 >> Host: localhost
headers - http-outgoing-0 >> Connection: Keep-Alive
headers - http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.3 (Java/1.8.0_131)
headers - http-outgoing-0 >> Accept-Encoding: gzip,deflate
headers - http-outgoing-0 << HTTP/1.1 401 Unauthorized
headers - http-outgoing-0 << Date: Mon, 10 Dec 2018 15:27:14 GMT
headers - http-outgoing-0 << Server: Apache/2.4.25 (Debian)
headers - http-outgoing-0 << WWW-Authenticate: Basic realm="Restricted Content"
headers - http-outgoing-0 << Content-Length: 456
headers - http-outgoing-0 << Keep-Alive: timeout=5, max=100
headers - http-outgoing-0 << Connection: Keep-Alive
headers - http-outgoing-0 << Content-Type: text/html; charset=iso-8859-1
MainClientExec - Connection can be kept alive for 5000 MILLISECONDS
HttpAuthenticator - Authentication required
HttpAuthenticator - localhost:80 requested authentication
TargetAuthenticationStrategy - Authentication schemes in the order of preference: [Negotiate, Kerberos, NTLM, Digest, Basic]
TargetAuthenticationStrategy - Challenge for Negotiate authentication scheme not available
TargetAuthenticationStrategy - Challenge for Kerberos authentication scheme not available
TargetAuthenticationStrategy - Challenge for NTLM authentication scheme not available
TargetAuthenticationStrategy - Challenge for Digest authentication scheme not available
HttpAuthenticator - Selected authentication options: [BASIC [complete=true]]
MainClientExec - Executing request GET /test/ HTTP/1.1
MainClientExec - Target auth state: CHALLENGED
HttpAuthenticator - Generating response to an authentication challenge using basic scheme
MainClientExec - Proxy auth state: UNCHALLENGED
headers - http-outgoing-0 >> GET /test/ HTTP/1.1
headers - http-outgoing-0 >> Host: localhost
headers - http-outgoing-0 >> Connection: Keep-Alive
headers - http-outgoing-0 >> User-Agent: Apache-HttpClient/4.5.3 (Java/1.8.0_131)
headers - http-outgoing-0 >> Accept-Encoding: gzip,deflate
headers - http-outgoing-0 >> Authorization: Basic *snip*
headers - http-outgoing-0 << HTTP/1.1 200 OK
headers - http-outgoing-0 << Date: Mon, 10 Dec 2018 15:27:14 GMT
headers - http-outgoing-0 << Server: Apache/2.4.25 (Debian)
headers - http-outgoing-0 << Vary: Accept-Encoding
headers - http-outgoing-0 << Content-Encoding: gzip
headers - http-outgoing-0 << Content-Length: 397
headers - http-outgoing-0 << Keep-Alive: timeout=5, max=99
headers - http-outgoing-0 << Connection: Keep-Alive
headers - http-outgoing-0 << Content-Type: text/html;charset=UTF-8
MainClientExec - Connection can be kept alive for 5000 MILLISECONDS
HttpAuthenticator - Authentication succeeded
TargetAuthenticationStrategy - Caching 'basic' auth scheme for http://localhost:80
Run Code Online (Sandbox Code Playgroud)

我想指出的是,客户提出了两次请求;一次失败401 Unauthorized,缺少Authorization: Basic标头,第二次失败,当客户端意识到需要授权时,会出现所需的标头。如果您根据初始请求来判断调试器,那么您可能判断得太快了。

  • 这很有帮助。进一步调试 API 后,我注意到第一次身份验证失败(通过故意发送错误的凭据),它返回 403 Forbidden。我相信它应该返回 401 Unauthorized。因此,也许(我在这里推测)HttpClient 在发送凭据之前期待 401 Unauthorized。如果是这种情况,则 API 编码不正确。我将尝试使用该 API,看看是否可以让它在身份验证失败时返回正确的响应。 (2认同)