wake-up-neo.com

HttpClientBuilder basic auth

Seit HttpClient 4.3 benutze ich den HttpClientBuilder. Ich verbinde mich mit einem REST - Dienst, der über eine Basisauthentifizierung verfügt. Ich lege die Anmeldeinformationen wie folgt fest:

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

Dies funktioniert jedoch nicht (der Dienst REST, den ich verwende, gibt 401 zurück). Was läuft schief?

16
Sayak Banerjee

Aus der Preemptive Authentication Dokumentation hier:

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

Standardmäßig stellt httpclient die Anmeldeinformationen nicht vorab bereit, sondern erstellt zuerst eine HTTP-Anforderung ohne Authentifizierungsparameter. Dies ist beabsichtigt, als Sicherheitsvorkehrung und als Teil der Spezifikation. Dies kann jedoch zu Problemen führen, wenn Sie die Verbindung nicht erneut versuchen oder wenn Sie eine Verbindung herstellen, von der Sie erwarten, dass Sie Authentifizierungsdetails für die erste Verbindung senden. Es verursacht auch eine zusätzliche Latenzzeit für eine Anforderung, da Sie mehrere Anrufe tätigen müssen, und 401s in den Protokollen angezeigt werden.

Sie können dieses Problem umgehen, indem Sie einen Authentifizierungscache verwenden, um so zu tun, als hätten Sie bereits eine Verbindung zum Server hergestellt. Das bedeutet, dass Sie nur einen HTTP-Aufruf tätigen und keine 401 in den Protokollen sehen: 

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

Bitte beachten Sie: Sie müssen dem Host, zu dem Sie eine Verbindung herstellen, vertrauen. Wenn Sie HTTP verwenden, werden Benutzername und Kennwort in Klartext gesendet (na ja, base64, aber das zählt nicht). 

Sie sollten auch ein viel spezifischeres Authscope verwenden, anstatt sich auf AuthScope .ANY_Host und AuthScope.ANY_PORT wie in Ihrem Beispiel zu verlassen.

35
Cetra

Da Sie dem Server bereits vertrauen, ist es wahrscheinlich am einfachsten, den Autorisierungsheader selbst zu erstellen.

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

Dies ist nur einer der Fälle, in denen es einfacher ist, die Spezifikation zu lesen und selbst zu rollen.

14
user2077221

Ich habe gerade Ihr Codebeispiel (gegen eine einfache, für Basic Auth aktivierte URL) ausprobiert, und es funktioniert einwandfrei - dies ist das Protokoll der HttpClient - zur Vereinfachung vereinfacht 

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]". 

Also - einfach gesagt: 
- Der Server fordert die erste Anforderung heraus 
- HttpClient erkennt das Basisauthentifizierungsschema und antwortet korrekt auf die Herausforderung 
- an diesem Punkt Server Server den erwarteten 200 OK 

Möglicherweise verwendet der von Ihnen verwendete REST - Dienst nicht die Standardauthentifizierung. Sie können versuchen, die vollständigen HttpClient-Protokolle einzufügen, um das Problem besser zu diagnostizieren. 

Hoffentlich hilft das. 

2
Eugen

Ich denke HttpClient ist wie eine andere Curl-basierte Lösung, es folgt der Spezifikation.

Und die Spezifikation lautet "Senden Sie die Anmeldeinformationen nicht, es sei denn, Sie werden vom Server dazu aufgefordert". Sie erhalten eine 401 ("Ich möchte, dass Sie die Anmeldeinformationen senden") ...

Es ist ein gewöhnliches Problem der Seife: Wenn Sie es nicht wissen, ist es nicht offensichtlich

1
farvilain