HTTPCLIENT-872: HttpClient can now persist authentication data between request executions as long as they share the same execution context. It has also become much easier to make HttpClient authenticate preemptively by pre-populating authentication data cache

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@881935 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2009-11-18 21:07:35 +00:00
parent b51b11bb91
commit f6699b27ff
13 changed files with 528 additions and 177 deletions

View File

@ -1,6 +1,12 @@
Changes since 4.0
-------------------
* [HTTPCLIENT-872] HttpClient can now persist authentication data between request
executions as long as they share the same execution context. It has also become
much easier to make HttpClient authenticate preemptively by pre-populating
authentication data cache.
Contributed by Oleg Kalnichevski <olegk at apache.org>
* [HTTPCLIENT-885] URLEncodedUtils now correctly parses form-url-encoded
entities that specify a charset.
Contributed by Oleg Kalnichevski <olegk at apache.org>

View File

@ -25,27 +25,18 @@
package org.apache.http.examples.client;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.AuthCache;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
/**
* An example of HttpClient can be customized to authenticate
@ -54,30 +45,29 @@ import org.apache.http.protocol.HttpContext;
* Generally, preemptive authentication can be considered less
* secure than a response to an authentication challenge
* and therefore discouraged.
* <b/>
* This code is NOT officially supported! Use at your risk.
*/
public class ClientPreemptiveBasicAuthentication {
public static void main(String[] args) throws Exception {
HttpHost targetHost = new HttpHost("localhost", 80, "http");
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.getCredentialsProvider().setCredentials(
new AuthScope("localhost", 80),
new AuthScope(targetHost.getHostName(), targetHost.getPort()),
new UsernamePasswordCredentials("username", "password"));
BasicHttpContext localcontext = new BasicHttpContext();
// Generate BASIC scheme object and stick it to the local
// execution context
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local
// auth cache
BasicScheme basicAuth = new BasicScheme();
localcontext.setAttribute("preemptive-auth", basicAuth);
authCache.put(targetHost, basicAuth);
// Add as the first request interceptor
httpclient.addRequestInterceptor(new PreemptiveAuth(), 0);
HttpHost targetHost = new HttpHost("localhost", 80, "http");
// Add AuthCache to the execution context
BasicHttpContext localcontext = new BasicHttpContext();
localcontext.setAttribute(ClientContext.AUTH_CACHE, authCache);
HttpGet httpget = new HttpGet("/");
@ -102,37 +92,4 @@ public class ClientPreemptiveBasicAuthentication {
httpclient.getConnectionManager().shutdown();
}
static class PreemptiveAuth implements HttpRequestInterceptor {
public void process(
final HttpRequest request,
final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(
ClientContext.TARGET_AUTH_STATE);
// If no auth scheme avaialble yet, try to initialize it preemptively
if (authState.getAuthScheme() == null) {
AuthScheme authScheme = (AuthScheme) context.getAttribute(
"preemptive-auth");
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
ClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(
ExecutionContext.HTTP_TARGET_HOST);
if (authScheme != null) {
Credentials creds = credsProvider.getCredentials(
new AuthScope(
targetHost.getHostName(),
targetHost.getPort()));
if (creds == null) {
throw new HttpException("No credentials for preemptive authentication");
}
authState.setAuthScheme(authScheme);
authState.setCredentials(creds);
}
}
}
}
}

View File

@ -25,28 +25,18 @@
package org.apache.http.examples.client;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.AuthCache;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.impl.auth.DigestScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
/**
* An example of HttpClient can be customized to authenticate
@ -55,35 +45,33 @@ import org.apache.http.protocol.HttpContext;
* Generally, preemptive authentication can be considered less
* secure than a response to an authentication challenge
* and therefore discouraged.
* <b/>
* This code is NOT officially supported! Use at your risk.
*/
public class ClientPreemptiveDigestAuthentication {
public static void main(String[] args) throws Exception {
HttpHost targetHost = new HttpHost("localhost", 80, "http");
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.getCredentialsProvider().setCredentials(
new AuthScope("localhost", 80),
new AuthScope(targetHost.getHostName(), targetHost.getPort()),
new UsernamePasswordCredentials("username", "password"));
BasicHttpContext localcontext = new BasicHttpContext();
// Generate DIGEST scheme object, initialize it and stick it to
// the local execution context
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate DIGEST scheme object, initialize it and add it to the local
// auth cache
DigestScheme digestAuth = new DigestScheme();
// Suppose we already know the realm name
digestAuth.overrideParamter("realm", "some realm");
// Suppose we already know the expected nonce value
digestAuth.overrideParamter("nonce", "whatever");
localcontext.setAttribute("preemptive-auth", digestAuth);
authCache.put(targetHost, digestAuth);
// Add as the first request interceptor
httpclient.addRequestInterceptor(new PreemptiveAuth(), 0);
// Add as the last response interceptor
httpclient.addResponseInterceptor(new PersistentDigest());
HttpHost targetHost = new HttpHost("localhost", 80, "http");
// Add AuthCache to the execution context
BasicHttpContext localcontext = new BasicHttpContext();
localcontext.setAttribute(ClientContext.AUTH_CACHE, authCache);
HttpGet httpget = new HttpGet("/");
@ -108,58 +96,4 @@ public class ClientPreemptiveDigestAuthentication {
httpclient.getConnectionManager().shutdown();
}
static class PreemptiveAuth implements HttpRequestInterceptor {
public void process(
final HttpRequest request,
final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(
ClientContext.TARGET_AUTH_STATE);
// If no auth scheme avaialble yet, try to initialize it preemptively
if (authState.getAuthScheme() == null) {
AuthScheme authScheme = (AuthScheme) context.getAttribute(
"preemptive-auth");
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
ClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(
ExecutionContext.HTTP_TARGET_HOST);
if (authScheme != null) {
Credentials creds = credsProvider.getCredentials(
new AuthScope(
targetHost.getHostName(),
targetHost.getPort()));
if (creds == null) {
throw new HttpException("No credentials for preemptive authentication");
}
authState.setAuthScheme(authScheme);
authState.setCredentials(creds);
}
}
}
}
static class PersistentDigest implements HttpResponseInterceptor {
public void process(
final HttpResponse response,
final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(
ClientContext.TARGET_AUTH_STATE);
if (authState != null) {
AuthScheme authScheme = authState.getAuthScheme();
// Stick the auth scheme to the local context, so
// we could try to authenticate subsequent requests
// preemptively
if (authScheme instanceof DigestScheme) {
context.setAttribute("preemptive-auth", authScheme);
}
}
}
}
}

View File

@ -0,0 +1,49 @@
/*
* ====================================================================
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.client;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScheme;
/**
* Abstract {@link AuthScheme} cache. Initialized {@link AuthScheme} objects
* from this cache can be used to preemptively authenticate against known
* hosts.
*
* @since 4.1
*/
public interface AuthCache {
void put(HttpHost host, AuthScheme authScheme);
AuthScheme get(HttpHost host);
void remove(HttpHost host);
void clear();
}

View File

@ -72,10 +72,16 @@ public interface ClientContext {
/**
* Attribute name of a {@link org.apache.http.client.CredentialsProvider}
* object that represents the actual crednetials provider.
* object that represents the actual credentials provider.
*/
public static final String CREDS_PROVIDER = "http.auth.credentials-provider";
/**
* Attribute name of a {@link org.apache.http.client.AuthCache} object
* that represents the auth scheme cache.
*/
public static final String AUTH_CACHE = "http.auth.auth-cache";
/**
* Attribute name of a {@link org.apache.http.auth.AuthState}
* object that represents the actual target authentication state.

View File

@ -0,0 +1,129 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.client.protocol;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.annotation.Immutable;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
/**
* Request interceptor that can preemptively authenticate against known hosts,
* if there is a cached {@link AuthScheme} instance in the local
* {@link AuthCache} associated with the given target or proxy host.
*
* @since 4.1
*/
@Immutable
public class RequestAuthCache implements HttpRequestInterceptor {
private final Log log = LogFactory.getLog(getClass());
public RequestAuthCache() {
super();
}
public void process(final HttpRequest request, final HttpContext context)
throws HttpException, IOException {
if (request == null) {
throw new IllegalArgumentException("HTTP request may not be null");
}
if (context == null) {
throw new IllegalArgumentException("HTTP context may not be null");
}
AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE);
if (authCache == null) {
context.setAttribute(ClientContext.AUTH_CACHE, new BasicAuthCache());
} else {
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
ClientContext.CREDS_PROVIDER);
HttpHost target = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
if (target != null) {
AuthScheme authScheme = authCache.get(target);
if (authScheme != null) {
doPreemptiveAuth(
target,
authScheme,
(AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE),
credsProvider);
}
}
HttpHost proxy = (HttpHost) context.getAttribute(ExecutionContext.HTTP_PROXY_HOST);
if (proxy != null) {
AuthScheme authScheme = authCache.get(proxy);
if (authScheme != null) {
doPreemptiveAuth(
proxy,
authScheme,
(AuthState) context.getAttribute(ClientContext.PROXY_AUTH_STATE),
credsProvider);
}
}
}
}
private void doPreemptiveAuth(
final HttpHost host,
final AuthScheme authScheme,
final AuthState authState,
final CredentialsProvider credsProvider) {
String schemeName = authScheme.getSchemeName();
if (this.log.isDebugEnabled()) {
this.log.debug("Re-using cached '" + schemeName + "' auth scheme for " + host);
}
Credentials creds = credsProvider.getCredentials(
new AuthScope(host.getHostName(), host.getPort(), schemeName));
if (creds != null) {
authState.setAuthScheme(authScheme);
authState.setCredentials(creds);
} else {
this.log.debug("No credentials for preemptive authentication");
}
}
}

View File

@ -0,0 +1,110 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.client.protocol;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.annotation.Immutable;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthState;
import org.apache.http.client.AuthCache;
import org.apache.http.client.params.AuthPolicy;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
/**
* Response interceptor that adds successfully completed {@link AuthScheme}s
* to the local {@link AuthCache} instance. Cached {@link AuthScheme}s can be
* re-used when executing requests against known hosts, thus avoiding
* additional authentication round-trips.
*
* @since 4.1
*/
@Immutable
public class ResponseAuthCache implements HttpResponseInterceptor {
private final Log log = LogFactory.getLog(getClass());
public ResponseAuthCache() {
super();
}
public void process(final HttpResponse response, final HttpContext context)
throws HttpException, IOException {
if (response == null) {
throw new IllegalArgumentException("HTTP request may not be null");
}
if (context == null) {
throw new IllegalArgumentException("HTTP context may not be null");
}
AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE);
if (authCache != null) {
cache(authCache,
(HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST),
(AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE));
cache(authCache,
(HttpHost) context.getAttribute(ExecutionContext.HTTP_PROXY_HOST),
(AuthState) context.getAttribute(ClientContext.PROXY_AUTH_STATE));
}
}
private void cache(final AuthCache authCache, final HttpHost host, final AuthState authState) {
if (authState == null) {
return;
}
AuthScheme authScheme = authState.getAuthScheme();
if (authScheme == null || !authScheme.isComplete()) {
return;
}
String schemeName = authScheme.getSchemeName();
if (!schemeName.equalsIgnoreCase(AuthPolicy.BASIC) &&
!schemeName.equalsIgnoreCase(AuthPolicy.DIGEST)) {
return;
}
if (authState.getAuthScope() != null) {
if (authState.getCredentials() != null) {
if (this.log.isDebugEnabled()) {
this.log.debug("Caching '" + schemeName + "' auth scheme for " + host);
}
authCache.put(host, authScheme);
} else {
authCache.remove(host);
}
}
}
}

View File

@ -0,0 +1,84 @@
/*
* ====================================================================
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.client;
import java.util.HashMap;
import org.apache.http.HttpHost;
import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.auth.AuthScheme;
import org.apache.http.client.AuthCache;
/**
* Default implementation of {@link AuthCache}.
*
* @since 4.0
*/
@NotThreadSafe
public class BasicAuthCache implements AuthCache {
private final HashMap<HttpHost, AuthScheme> map;
/**
* Default constructor.
*/
public BasicAuthCache() {
super();
this.map = new HashMap<HttpHost, AuthScheme>();
}
public void put(final HttpHost host, final AuthScheme authScheme) {
if (host == null) {
throw new IllegalArgumentException("HTTP host may not be null");
}
this.map.put(host, authScheme);
}
public AuthScheme get(final HttpHost host) {
if (host == null) {
throw new IllegalArgumentException("HTTP host may not be null");
}
return this.map.get(host);
}
public void remove(final HttpHost host) {
if (host == null) {
throw new IllegalArgumentException("HTTP host may not be null");
}
this.map.remove(host);
}
public void clear() {
this.map.clear();
}
@Override
public String toString() {
return this.map.toString();
}
}

View File

@ -102,13 +102,13 @@ public class BasicCredentialsProvider implements CredentialsProvider {
return matchCredentials(this.credMap, authscope);
}
public synchronized void clear() {
this.credMap.clear();
}
@Override
public String toString() {
return credMap.toString();
}
public synchronized void clear() {
this.credMap.clear();
}
}

View File

@ -43,10 +43,12 @@ import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.client.protocol.RequestAddCookies;
import org.apache.http.client.protocol.RequestAuthCache;
import org.apache.http.client.protocol.RequestClientConnControl;
import org.apache.http.client.protocol.RequestDefaultHeaders;
import org.apache.http.client.protocol.RequestProxyAuthentication;
import org.apache.http.client.protocol.RequestTargetAuthentication;
import org.apache.http.client.protocol.ResponseAuthCache;
import org.apache.http.client.protocol.ResponseProcessCookies;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ClientConnectionManagerFactory;
@ -337,6 +339,8 @@ public class DefaultHttpClient extends AbstractHttpClient {
httpproc.addInterceptor(new RequestAddCookies());
httpproc.addInterceptor(new ResponseProcessCookies());
// HTTP authentication interceptors
httpproc.addInterceptor(new RequestAuthCache());
httpproc.addInterceptor(new ResponseAuthCache());
httpproc.addInterceptor(new RequestTargetAuthentication());
httpproc.addInterceptor(new RequestProxyAuthentication());
return httpproc;

View File

@ -50,6 +50,7 @@ import org.apache.http.localserver.BasicServerTestBase;
import org.apache.http.localserver.LocalTestServer;
import org.apache.http.localserver.RequestBasicAuth;
import org.apache.http.localserver.ResponseBasicUnauthorized;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
@ -248,4 +249,67 @@ public class TestClientAuthentication extends BasicServerTestBase {
}
}
static class TestTargetAuthenticationHandler extends DefaultTargetAuthenticationHandler {
private int count;
public TestTargetAuthenticationHandler() {
super();
this.count = 0;
}
@Override
public boolean isAuthenticationRequested(
final HttpResponse response,
final HttpContext context) {
boolean res = super.isAuthenticationRequested(response, context);
if (res == true) {
synchronized (this) {
this.count++;
}
}
return res;
}
public int getCount() {
synchronized (this) {
return this.count;
}
}
}
public void testBasicAuthenticationCredentialsCaching() throws Exception {
localServer.register("*", new AuthHandler());
localServer.start();
BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials("test", "test"));
TestTargetAuthenticationHandler authHandler = new TestTargetAuthenticationHandler();
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.setCredentialsProvider(credsProvider);
httpclient.setTargetAuthenticationHandler(authHandler);
HttpContext context = new BasicHttpContext();
HttpGet httpget = new HttpGet("/");
HttpResponse response1 = httpclient.execute(getServerHttp(), httpget, context);
HttpEntity entity1 = response1.getEntity();
assertEquals(HttpStatus.SC_OK, response1.getStatusLine().getStatusCode());
assertNotNull(entity1);
entity1.consumeContent();
HttpResponse response2 = httpclient.execute(getServerHttp(), httpget, context);
HttpEntity entity2 = response1.getEntity();
assertEquals(HttpStatus.SC_OK, response2.getStatusLine().getStatusCode());
assertNotNull(entity2);
entity1.consumeContent();
assertEquals(1, authHandler.getCount());
}
}

View File

@ -47,6 +47,8 @@ public class RequestBasicAuth implements HttpRequestInterceptor {
final HttpRequest request,
final HttpContext context) throws HttpException, IOException {
context.removeAttribute("creds");
String auth = null;
Header h = request.getFirstHeader(AUTH.WWW_AUTH_RESP);

View File

@ -305,6 +305,14 @@ null
takes precedence over the default one.</para>
</formalpara>
</listitem>
<listitem>
<formalpara>
<title>'http.auth.auth-cache':</title>
<para><interfacename>AuthCache</interfacename> instance representing the actual
authentication data cache. The value of this attribute set in the local
context takes precedence over the default one.</para>
</formalpara>
</listitem>
</itemizedlist>
<para>The local <interfacename>HttpContext</interfacename> object can be used to customize
the HTTP authentication context prior to request execution or examine its state after
@ -327,6 +335,14 @@ System.out.println("Target auth scheme: " + targetAuthState.getAuthScheme());
System.out.println("Target auth credentials: " + targetAuthState.getCredentials());
]]></programlisting>
</section>
<section>
<title>Caching of authentication data</title>
<para>As of version 4.1 HttpClient automatically caches information about hosts it
successfully authenticated with. Please note that one must use the same execution
context to execute logically related requests in order for cached authentication data
to propagate from one request to another. Authentication data will be lost as soon as
the execution context goes out of scope.</para>
</section>
<section>
<title>Preemptive authentication</title>
<para>HttpClient does not support preemptive authentication out of the box, because if
@ -334,46 +350,36 @@ System.out.println("Target auth credentials: " + targetAuthState.getCredentials(
security issues, such as sending user credentials in clear text to an unauthorized third
party. Therefore, users are expected to evaluate potential benefits of preemptive
authentication versus security risks in the context of their specific application
environment and are required to add support for preemptive authentication using standard
HttpClient extension mechanisms such as protocol interceptors.</para>
<para>This is an example of a simple protocol interceptor that preemptively introduces an
instance of <classname>BasicScheme</classname> to the execution context, if no
authentication has been attempted yet. Please note that this interceptor must be added
to the protocol processing chain before the standard authentication interceptors.</para>
environment.</para>
<para>Nonethess one can configure HttpClient to authenticate preemptively by prepopulating
the authentication data cache.</para>
<programlisting><![CDATA[
HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor() {
public void process(
final HttpRequest request,
final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(
ClientContext.TARGET_AUTH_STATE);
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
ClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(
ExecutionContext.HTTP_TARGET_HOST);
// If not auth scheme has been initialized yet
if (authState.getAuthScheme() == null) {
AuthScope authScope = new AuthScope(
targetHost.getHostName(),
targetHost.getPort());
// Obtain credentials matching the target host
Credentials creds = credsProvider.getCredentials(authScope);
// If found, generate BasicScheme preemptively
if (creds != null) {
authState.setAuthScheme(new BasicScheme());
authState.setCredentials(creds);
}
}
}
};
HttpHost targetHost = new HttpHost("localhost", 80, "http");
DefaultHttpClient httpclient = new DefaultHttpClient();
// Add as the very first interceptor in the protocol chain
httpclient.addRequestInterceptor(preemptiveAuth, 0);
httpclient.getCredentialsProvider().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
BasicHttpContext localcontext = new BasicHttpContext();
localcontext.setAttribute(ClientContext.AUTH_CACHE, authCache);
HttpGet httpget = new HttpGet("/");
for (int i = 0; i < 3; i++) {
HttpResponse response = httpclient.execute(targetHost, httpget, localcontext);
HttpEntity entity = response.getEntity();
if (entity != null) {
entity.consumeContent();
}
}
]]></programlisting>
</section>