【问题标题】:HttpClientBuilder basic authHttpClientBuilder 基本身份验证
【发布时间】:2014-01-21 18:03:14
【问题描述】:

从 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);
}

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

【问题讨论】:

    标签: java apache basic-authentication apache-httpclient-4.x


    【解决方案1】:

    来自 Preemptive Authentication 文档:

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

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

    解决方法是使用身份验证缓存来假装您已经连接到服务器一次。这意味着您只会进行一次 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();
        }
    }
    

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

    您还应该使用更具体的 Authscope,而不是像您的示例中那样依赖 AuthScope .ANY_HOST 和 AuthScope.ANY_PORT。

    【讨论】:

    • 出于好奇,为什么请求执行了3次?
    • 我直接从原始apache tutorial复制代码,没有其他原因。
    • 阅读 5 篇关于 HttpPost 的基本身份验证帖子,终于有了解决方案!
    【解决方案2】:

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

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

    这只是其中一种情况,更容易阅读spec,并自行滚动。

    【讨论】:

    • 但是为什么不使用可用的标准机制呢?使用 BasicCredentialsProvider() 和 BasicAuthCache() 并不复杂...
    【解决方案3】:

    我刚刚尝试了您的代码示例(针对一个简单的启用基本身份验证的 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]". 
    

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

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

    希望对您有所帮助。

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

      • 对不起,但这个答案对我没有任何帮助。你提出什么解决方案?
      • 没什么,客户端得到 401 并使用凭据重做请求。你能再检查一下没有第二个请求并给出调用代码,而不仅仅是客户端的创建。
      猜你喜欢
      • 2021-06-20
      • 2011-01-28
      • 1970-01-01
      • 1970-01-01
      • 2021-06-17
      • 2014-09-03
      • 2012-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多