Java HttpClientBuilder 基本身份验证

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20914311/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 05:17:27  来源:igfitidea点击:

HttpClientBuilder basic auth

javaapachebasic-authenticationapache-httpclient-4.x

提问by Sayak Banerjee

Since HttpClient 4.3, I have been using the HttpClientBuilder. I am connecting to a REST service that has basic authentication. I am setting the credentials as follows:

从 HttpClient 4.3 开始,我一直在使用 HttpClientBuilder。我正在连接到具有基本身份验证的 REST 服务。我正在设置凭据如下:

HttpClientBuilder builder = HttpClientBuilder.create();

// Get the client credentials
String username = Config.get(Constants.CONFIG_USERNAME);
String password = Config.get(Constants.CONFIG_PASSWORD);

// If username and password was found, inject the credentials
if (username != null && password != null)
{
    CredentialsProvider provider = new BasicCredentialsProvider();

    // Create the authentication scope
    AuthScope scope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM);

    // Create credential pair
    UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);

    // Inject the credentials
    provider.setCredentials(scope, credentials);

    // Set the default credentials provider
    builder.setDefaultCredentialsProvider(provider);
}

However, this does not work (the REST service that I am using is returning 401). What is going wrong?

但是,这不起作用(我正在使用的 REST 服务返回 401)。出了什么问题?

采纳答案by Cetra

From the Preemptive AuthenticationDocumentation here:

从这里的抢先身份验证文档:

http://hc.apache.org/httpcomponents-client-ga/tutorial/html/authentication.html

http://hc.apache.org/httpcomponents-client-ga/tutorial/html/authentication.html

By default, httpclient will not provide credentials preemptively, it will first create a HTTP request without authentication parameters. This is by design, as a security precaution, and as part of the spec. But, this causes issues if you don't retry the connection, or wherever you're connecting to expects you to send authentication details on the first connection. It also causes extra latency to a request, as you need to make multiple calls, and causes 401s to appear in the logs.

默认情况下,httpclient 不会抢先提供凭据,它会先创建一个没有身份验证参数的 HTTP 请求。这是设计使然,作为安全预防措施,并且作为规范的一部分。但是,如果您不重试连接,或者您连接到的任何地方都希望您在第一次连接时发送身份验证详细信息,则会导致问题。它还会给请求造成额外的延迟,因为您需要进行多次调用,并导致 401 出现在日志中。

The workaround is to use an authentication cache to pretend that you've already connected to the server once. This means you'll only make one HTTP call and won't see a 401 in the logs:

解决方法是使用身份验证缓存来假装您已经连接到服务器一次。这意味着您只会进行一次 HTTP 调用,并且不会在日志中看到 401:

CloseableHttpClient httpclient = HttpClientBuilder.create().build();

HttpHost targetHost = new HttpHost("localhost", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
        new AuthScope(targetHost.getHostName(), targetHost.getPort()),
        new UsernamePasswordCredentials("username", "password"));

// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local auth cache
BasicScheme basicAuth = new BasicScheme();
authCache.put(targetHost, basicAuth);

// Add AuthCache to the execution context
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);

HttpGet httpget = new HttpGet("/");
for (int i = 0; i < 3; i++) {
    CloseableHttpResponse response = httpclient.execute(
            targetHost, httpget, context);
    try {
        HttpEntity entity = response.getEntity();

    } finally {
        response.close();
    }
}

Please note: You need to trust the host you're connecting to, and if you're using HTTP, your username and password will be sent in cleartext (well, base64, but that doesn't count).

请注意:您需要信任您正在连接的主机,如果您使用 HTTP,您的用户名和密码将以明文形式发送(好吧,base64,但这不算数)。

You should also be using a much more specific Authscope rather than relying on AuthScope .ANY_HOSTand AuthScope.ANY_PORTlike in your example.

您还应该使用更具体的 Authscope,而不是依赖AuthScope .ANY_HOSTAuthScope.ANY_PORT喜欢您的示例。

回答by farvilain

I thinkHttpClient is like other curl based solution, it follows the spec.

我认为HttpClient 就像其他基于 curl 的解决方案一样,它遵循规范。

And the spec is "Do not send the credentials except if server tell you to do so". So you get a 401 ("I want u to send credentials")...

规范是“除非服务器告诉您这样做,否则不要发送凭据”。所以你得到一个 401(“我希望你发送凭据”)......

It's a common soap ui problem: when you don't know, it's not obvious

这是一个常见的soap ui问题:当你不知道时,它并不明显

回答by Eugen

I just tried your code sample (against a simple Basic Auth enabled URL) and it works fine - this is the log from the HttpClient- a bit simplified for brevity:

我刚刚尝试了你的代码示例(针对一个简单的启用基本身份验证的 URL)并且它工作正常 - 这是来自HttpClient- 为简洁起见有点简化的日志:

web - 2014-01-04 12:43:19,700 [main] DEBUG o.a.h.c.protocol.RequestAddCookies - CookieSpec selected: best-match
web - 2014-01-04 12:43:19,710 [main] DEBUG o.a.h.c.protocol.RequestAuthCache - Auth cache not set in the context
web - 2014-01-04 12:43:19,728 [main] DEBUG o.a.h.impl.execchain.MainClientExec - Opening connection {}->http://localhost:8080
web - 2014-01-04 12:43:19,730 [main] DEBUG o.a.h.c.HttpClientConnectionManager - Connecting to localhost/127.0.0.1:8080
web - 2014-01-04 12:43:19,731 [main] DEBUG o.a.h.impl.execchain.MainClientExec - Executing request GET /spring-security-mvc-basic-auth/homepage.html HTTP/1.1
web - 2014-01-04 12:43:19,731 [main] DEBUG o.a.h.impl.execchain.MainClientExec - Target auth state: UNCHALLENGED
web - 2014-01-04 12:43:19,731 [main] DEBUG o.a.h.impl.execchain.MainClientExec - Proxy auth state: UNCHALLENGED
web - 2014-01-04 12:43:19,732 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> GET /spring-security-mvc-basic-auth/homepage.html HTTP/1.1
web - 2014-01-04 12:43:19,732 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> Host: localhost:8080
web - 2014-01-04 12:43:19,732 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> User-Agent: Apache-HttpClient/4.3.1 (java 1.5)
web - 2014-01-04 12:43:19,735 [main] DEBUG org.apache.http.headers - http-outgoing-0 << HTTP/1.1 401 Unauthorized
web - 2014-01-04 12:43:19,735 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Server: Apache-Coyote/1.1
web - 2014-01-04 12:43:19,735 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Set-Cookie: JSESSIONID=B8E6D0D7DE0C99991A74E9B2E4EA68AE; Path=/spring-security-mvc-basic-auth/; HttpOnly
web - 2014-01-04 12:43:19,735 [main] DEBUG org.apache.http.headers - http-outgoing-0 << WWW-Authenticate: Basic realm="Baeldung"
web - 2014-01-04 12:43:19,735 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Content-Length: 75
web - 2014-01-04 12:43:19,735 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Date: Sat, 04 Jan 2014 10:43:19 GMT
web - 2014-01-04 12:43:19,738 [main] DEBUG o.a.http.impl.auth.HttpAuthenticator - Authentication required
web - 2014-01-04 12:43:19,738 [main] DEBUG o.a.http.impl.auth.HttpAuthenticator - localhost:8080 requested authentication
web - 2014-01-04 12:43:19,738 [main] DEBUG o.a.h.i.c.TargetAuthenticationStrategy - Authentication schemes in the order of preference: [negotiate, Kerberos, NTLM, Digest, Basic]
web - 2014-01-04 12:43:19,738 [main] DEBUG o.a.h.i.c.TargetAuthenticationStrategy - Challenge for negotiate authentication scheme not available
web - 2014-01-04 12:43:19,738 [main] DEBUG o.a.h.i.c.TargetAuthenticationStrategy - Challenge for Kerberos authentication scheme not available
web - 2014-01-04 12:43:19,738 [main] DEBUG o.a.h.i.c.TargetAuthenticationStrategy - Challenge for NTLM authentication scheme not available
web - 2014-01-04 12:43:19,738 [main] DEBUG o.a.h.i.c.TargetAuthenticationStrategy - Challenge for Digest authentication scheme not available
web - 2014-01-04 12:43:19,745 [main] DEBUG o.a.http.impl.auth.HttpAuthenticator - Selected authentication options: [BASIC]
web - 2014-01-04 12:43:19,746 [main] DEBUG o.a.h.impl.execchain.MainClientExec - Executing request GET /spring-security-mvc-basic-auth/homepage.html HTTP/1.1
web - 2014-01-04 12:43:19,746 [main] DEBUG o.a.h.impl.execchain.MainClientExec - Target auth state: CHALLENGED
web - 2014-01-04 12:43:19,746 [main] DEBUG o.a.http.impl.auth.HttpAuthenticator - Generating response to an authentication challenge using basic scheme
web - 2014-01-04 12:43:19,747 [main] DEBUG o.a.h.impl.execchain.MainClientExec - Proxy auth state: UNCHALLENGED
web - 2014-01-04 12:43:19,747 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> GET /spring-security-mvc-basic-auth/homepage.html HTTP/1.1
web - 2014-01-04 12:43:19,747 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> Host: localhost:8080
web - 2014-01-04 12:43:19,747 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> User-Agent: Apache-HttpClient/4.3.1 (java 1.5)
web - 2014-01-04 12:43:19,747 [main] DEBUG org.apache.http.headers - http-outgoing-0 >> Authorization: Basic dXNlcjE6dXNlcjFQYXNz
web - 2014-01-04 12:43:19,750 [main] DEBUG org.apache.http.headers - http-outgoing-0 << HTTP/1.1 200 OK
web - 2014-01-04 12:43:19,750 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Server: Apache-Coyote/1.1
web - 2014-01-04 12:43:19,750 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Set-Cookie: JSESSIONID=C03FD4EB1421A4C3A003ADC895D49599; Path=/spring-security-mvc-basic-auth/; HttpOnly
web - 2014-01-04 12:43:19,750 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Content-Type: text/html;charset=ISO-8859-1
web - 2014-01-04 12:43:19,750 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Content-Language: en-US
web - 2014-01-04 12:43:19,751 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Content-Length: 96
web - 2014-01-04 12:43:19,751 [main] DEBUG org.apache.http.headers - http-outgoing-0 << Date: Sat, 04 Jan 2014 10:43:19 GMT
web - 2014-01-04 12:43:19,751 [main] DEBUG o.a.http.impl.auth.HttpAuthenticator - Authentication succeeded
web - 2014-01-04 12:43:19,751 [main] DEBUG o.a.h.i.c.TargetAuthenticationStrategy - Caching 'basic' auth scheme for http://localhost:8080
web - 2014-01-04 12:43:19,760 [main] DEBUG o.a.h.c.p.ResponseProcessCookies - Cookie accepted: "[version: 0][name: JSESSIONID][value: C03FD4EB1421A4C3A003ADC895D49599][domain: localhost][path: /spring-security-mvc-basic-auth/][expiry: null]". 

So - simply put:
- the Server does challenge the initial request
- HttpClientrecognizes the Basic Auth scheme and responds to the challenge correctly
- at that point the Server servers the expected 200 OK

所以 - 简单地说:
- 服务器确实挑战了初始请求
-HttpClient识别基本身份验证方案并正确响应挑战
- 在这一点上,服务器服务器提供预期的200 OK

It may be the case that the REST Service you're using isn't actually using Basic Authentication. You can try to paste the full HttpClientlogs to better diagnose the issue.

可能是您使用的 REST 服务实际上并未使用基本身份验证。您可以尝试粘贴完整HttpClient日志以更好地诊断问题。

Hope that helps.

希望有帮助。

回答by user2077221

Actually, since you already trust the server, it's probably easiest to just construct the authorization header yourself.

实际上,由于您已经信任服务器,因此自己构建授权标头可能是最简单的。

 byte[] credentials = Base64.encodeBase64((username + ":" + password).getBytes(StandardCharsets.UTF_8));
 request.setHeader("Authorization", "Basic " + new String(credentials, StandardCharsets.UTF_8));
 httpClient.execute(request);

This is just one of those cases were it's easier to read the spec, and roll it yourself.

这只是其中一种情况,因为它更容易阅读规范并自己滚动。