HTTPCLIENT-477: Use distinct instances of the authentication handler interface for authentication with target and proxy hosts

Contributed by Oleg Kalnichevski


git-svn-id: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpclient/trunk@585984 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2007-10-18 14:44:26 +00:00
parent b0f91905f4
commit bf541ba1fd
9 changed files with 242 additions and 130 deletions

View File

@ -1,5 +1,9 @@
Changes since release 4.0 Alpha 1
* [HTTPCLIENT-477] Use distinct instances of the authentication handler
interface for authentication with target and proxy hosts
Contributed by Oleg Kalnichevski <olegk at apache.org>
* [HTTPCLIENT-690] ManagedClientConnection provides access to SSLSession
Contributed by Roland Weber <rolandw at apache.org>

View File

@ -44,25 +44,12 @@ import org.apache.http.protocol.HttpContext;
*/
public interface AuthenticationHandler {
boolean isTargetAuthenticationRequested(
HttpResponse response,
HttpContext context);
boolean isAuthenticationRequested(HttpResponse response, HttpContext context);
boolean isProxyAuthenticationRequested(
HttpResponse response,
HttpContext context);
Map getChallenges(HttpResponse response, HttpContext context)
throws MalformedChallengeException;
Map getTargetChallenges(
HttpResponse response,
HttpContext context) throws MalformedChallengeException;
Map getProxyChallenges(
HttpResponse response,
HttpContext context) throws MalformedChallengeException;
AuthScheme selectScheme(
Map challenges,
HttpResponse response,
HttpContext context) throws AuthenticationException;
AuthScheme selectScheme(Map challenges, HttpResponse response, HttpContext context)
throws AuthenticationException;
}

View File

@ -111,23 +111,6 @@ public interface ClientPNames {
*/
public static final String PREEMPTIVE_AUTHENTICATION = "http.protocol.authentication-preemptive";
/**
* The key used to look up the list of IDs of supported
* {@link AuthPolicy authentication schemes} in their order of preference.
* The scheme IDs are stored in a {@link java.util.Collection} as
* instances of {@link java.lang.String}.
*
* <p>
* If several schemes are returned in the <tt>WWW-Authenticate</tt>
* or <tt>Proxy-Authenticate</tt> header, this parameter defines which
* {@link AuthPolicy authentication schemes} takes precedence over others.
* The first item in the collection represents the most preferred
* {@link AuthPolicy authentication scheme}, the last item represents
* the ID of the least preferred one.
* </p>
*/
public static final String AUTH_SCHEME_PRIORITY = "http.protocol-auth-scheme-priority";
/**
* Defines the name of the cookie specification to be used for HTTP state management.
* <p>
@ -162,6 +145,5 @@ public interface ClientPNames {
*/
public static final String DEFAULT_HOST = "http.default-host";
}

View File

@ -32,7 +32,6 @@
package org.apache.http.impl.client;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -40,19 +39,15 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.FormattedHeader;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AUTH;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthSchemeRegistry;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.client.AuthenticationHandler;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.CharArrayBuffer;
@ -60,39 +55,19 @@ import org.apache.http.util.CharArrayBuffer;
/**
* @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
*/
public class DefaultAuthenticationHandler implements AuthenticationHandler {
public abstract class AbstractAuthenticationHandler implements AuthenticationHandler {
private static final Log LOG = LogFactory.getLog(DefaultAuthenticationHandler.class);
private static final Log LOG = LogFactory.getLog(AbstractAuthenticationHandler.class);
private static List DEFAULT_SCHEME_PRIORITY = Arrays.asList(new String[] {
"digest",
"basic"
});
public DefaultAuthenticationHandler() {
public AbstractAuthenticationHandler() {
super();
}
public boolean isTargetAuthenticationRequested(
final HttpResponse response,
final HttpContext context) {
if (response == null) {
throw new IllegalArgumentException("HTTP response may not be null");
}
int status = response.getStatusLine().getStatusCode();
return status == HttpStatus.SC_UNAUTHORIZED;
}
public boolean isProxyAuthenticationRequested(
final HttpResponse response,
final HttpContext context) {
if (response == null) {
throw new IllegalArgumentException("HTTP response may not be null");
}
int status = response.getStatusLine().getStatusCode();
return status == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED;
}
protected Map parseChallenges(
final Header[] headers) throws MalformedChallengeException {
@ -127,26 +102,10 @@ public class DefaultAuthenticationHandler implements AuthenticationHandler {
return map;
}
public Map getTargetChallenges(
final HttpResponse response,
final HttpContext context) throws MalformedChallengeException {
if (response == null) {
throw new IllegalArgumentException("HTTP response may not be null");
}
Header[] headers = response.getHeaders(AUTH.WWW_AUTH);
return parseChallenges(headers);
protected List getAuthPreferences() {
return DEFAULT_SCHEME_PRIORITY;
}
public Map getProxyChallenges(
final HttpResponse response,
final HttpContext context) throws MalformedChallengeException {
if (response == null) {
throw new IllegalArgumentException("HTTP response may not be null");
}
Header[] headers = response.getHeaders(AUTH.PROXY_AUTH);
return parseChallenges(headers);
}
public AuthScheme selectScheme(
final Map challenges,
final HttpResponse response,
@ -158,12 +117,7 @@ public class DefaultAuthenticationHandler implements AuthenticationHandler {
throw new IllegalStateException("AuthScheme registry not set in HTTP context");
}
HttpParams params = response.getParams();
Collection authPrefs = (Collection) params.getParameter(
ClientPNames.AUTH_SCHEME_PRIORITY);
if (authPrefs == null || authPrefs.isEmpty()) {
authPrefs = DEFAULT_SCHEME_PRIORITY;
}
List authPrefs = getAuthPreferences();
if (LOG.isDebugEnabled()) {
LOG.debug("Supported authentication schemes in the order of preference: "
+ authPrefs);
@ -178,7 +132,7 @@ public class DefaultAuthenticationHandler implements AuthenticationHandler {
LOG.debug(id + " authentication scheme selected");
}
try {
authScheme = registry.getAuthScheme(id, params);
authScheme = registry.getAuthScheme(id, response.getParams());
} catch (IllegalStateException e) {
throw new AuthenticationException(e.getMessage());
}

View File

@ -106,8 +106,11 @@ public abstract class AbstractHttpClient
/** The redirect handler. */
private RedirectHandler redirectHandler;
/** The authentication handler. */
private AuthenticationHandler authHandler;
/** The target authentication handler. */
private AuthenticationHandler targetAuthHandler;
/** The proxy authentication handler. */
private AuthenticationHandler proxyAuthHandler;
/** The cookie store. */
private CookieStore cookieStore;
@ -159,7 +162,10 @@ public abstract class AbstractHttpClient
protected abstract RedirectHandler createRedirectHandler();
protected abstract AuthenticationHandler createAuthenticationHandler();
protected abstract AuthenticationHandler createTargetAuthenticationHandler();
protected abstract AuthenticationHandler createProxyAuthenticationHandler();
protected abstract CookieStore createCookieStore();
@ -285,16 +291,31 @@ public abstract class AbstractHttpClient
}
public synchronized final AuthenticationHandler getAuthenticationHandler() {
if (authHandler == null) {
authHandler = createAuthenticationHandler();
public synchronized final AuthenticationHandler getTargetAuthenticationHandler() {
if (targetAuthHandler == null) {
targetAuthHandler = createTargetAuthenticationHandler();
}
return authHandler;
return targetAuthHandler;
}
public synchronized void setAuthenticationHandler(final AuthenticationHandler authHandler) {
this.authHandler = authHandler;
public synchronized void setTargetAuthenticationHandler(
final AuthenticationHandler targetAuthHandler) {
this.targetAuthHandler = targetAuthHandler;
}
public synchronized final AuthenticationHandler getProxyAuthenticationHandler() {
if (proxyAuthHandler == null) {
proxyAuthHandler = createProxyAuthenticationHandler();
}
return proxyAuthHandler;
}
public synchronized void setProxyAuthenticationHandler(
final AuthenticationHandler proxyAuthHandler) {
this.proxyAuthHandler = proxyAuthHandler;
}
@ -495,7 +516,8 @@ public abstract class AbstractHttpClient
getHttpProcessor().copy(),
getHttpRequestRetryHandler(),
getRedirectHandler(),
getAuthenticationHandler(),
getTargetAuthenticationHandler(),
getProxyAuthenticationHandler(),
getParams());
}

View File

@ -123,8 +123,11 @@ public class DefaultClientRequestDirector
/** The redirect handler. */
protected final RedirectHandler redirectHandler;
/** The authentication handler. */
private final AuthenticationHandler authHandler;
/** The target authentication handler. */
private final AuthenticationHandler targetAuthHandler;
/** The proxy authentication handler. */
private final AuthenticationHandler proxyAuthHandler;
/** The HTTP parameters. */
protected final HttpParams params;
@ -146,7 +149,8 @@ public class DefaultClientRequestDirector
final HttpProcessor httpProcessor,
final HttpRequestRetryHandler retryHandler,
final RedirectHandler redirectHandler,
final AuthenticationHandler authHandler,
final AuthenticationHandler targetAuthHandler,
final AuthenticationHandler proxyAuthHandler,
final HttpParams params) {
if (conman == null) {
@ -164,22 +168,26 @@ public class DefaultClientRequestDirector
if (redirectHandler == null) {
throw new IllegalArgumentException("Redirect handler may not be null");
}
if (authHandler == null) {
throw new IllegalArgumentException("Authentication handler may not be null");
if (targetAuthHandler == null) {
throw new IllegalArgumentException("Target authentication handler may not be null");
}
if (proxyAuthHandler == null) {
throw new IllegalArgumentException("Proxy authentication handler may not be null");
}
if (params == null) {
throw new IllegalArgumentException("HTTP parameters may not be null");
}
this.connManager = conman;
this.reuseStrategy = reustrat;
this.httpProcessor = httpProcessor;
this.retryHandler = retryHandler;
this.redirectHandler = redirectHandler;
this.authHandler = authHandler;
this.params = params;
this.requestExec = new HttpRequestExecutor();
this.connManager = conman;
this.reuseStrategy = reustrat;
this.httpProcessor = httpProcessor;
this.retryHandler = retryHandler;
this.redirectHandler = redirectHandler;
this.targetAuthHandler = targetAuthHandler;
this.proxyAuthHandler = proxyAuthHandler;
this.params = params;
this.requestExec = new HttpRequestExecutor();
this.managedConn = null;
this.managedConn = null;
this.redirectCount = 0;
this.maxRedirects = this.params.getIntParameter(ClientPNames.MAX_REDIRECTS, 100);
@ -596,12 +604,14 @@ public class DefaultClientRequestDirector
context.getAttribute(ClientContext.CREDS_PROVIDER);
if (credsProvider != null && HttpClientParams.isAuthenticating(params)) {
if (this.authHandler.isProxyAuthenticationRequested(response, context)) {
if (this.proxyAuthHandler.isAuthenticationRequested(response, context)) {
LOG.debug("Proxy requested authentication");
Map challenges = this.authHandler.getProxyChallenges(response, context);
Map challenges = this.proxyAuthHandler.getChallenges(response, context);
try {
processChallenges(challenges, this.proxyAuthState, response, context);
processChallenges(
challenges, this.proxyAuthState, this.proxyAuthHandler,
response, context);
} catch (AuthenticationException ex) {
if (LOG.isWarnEnabled()) {
LOG.warn("Authentication error: " + ex.getMessage());
@ -805,7 +815,7 @@ public class DefaultClientRequestDirector
if (credsProvider != null && HttpClientParams.isAuthenticating(params)) {
if (this.authHandler.isTargetAuthenticationRequested(response, context)) {
if (this.targetAuthHandler.isAuthenticationRequested(response, context)) {
target = (HttpHost)
context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
@ -814,9 +824,11 @@ public class DefaultClientRequestDirector
}
LOG.debug("Target requested authentication");
Map challenges = this.authHandler.getTargetChallenges(response, context);
Map challenges = this.targetAuthHandler.getChallenges(response, context);
try {
processChallenges(challenges, this.targetAuthState, response, context);
processChallenges(challenges,
this.targetAuthState, this.targetAuthHandler,
response, context);
} catch (AuthenticationException ex) {
if (LOG.isWarnEnabled()) {
LOG.warn("Authentication error: " + ex.getMessage());
@ -836,12 +848,14 @@ public class DefaultClientRequestDirector
this.targetAuthState.setAuthScope(null);
}
if (this.authHandler.isProxyAuthenticationRequested(response, context)) {
if (this.proxyAuthHandler.isAuthenticationRequested(response, context)) {
LOG.debug("Proxy requested authentication");
Map challenges = this.authHandler.getProxyChallenges(response, context);
Map challenges = this.proxyAuthHandler.getChallenges(response, context);
try {
processChallenges(challenges, this.proxyAuthState, response, context);
processChallenges(challenges,
this.proxyAuthState, this.proxyAuthHandler,
response, context);
} catch (AuthenticationException ex) {
if (LOG.isWarnEnabled()) {
LOG.warn("Authentication error: " + ex.getMessage());
@ -895,6 +909,7 @@ public class DefaultClientRequestDirector
private void processChallenges(
final Map challenges,
final AuthState authState,
final AuthenticationHandler authHandler,
final HttpResponse response,
final HttpContext context)
throws MalformedChallengeException, AuthenticationException {
@ -902,18 +917,17 @@ public class DefaultClientRequestDirector
AuthScheme authScheme = authState.getAuthScheme();
if (authScheme == null) {
// Authentication not attempted before
authScheme = this.authHandler.selectScheme(challenges, response, context);
authScheme = authHandler.selectScheme(challenges, response, context);
authState.setAuthScheme(authScheme);
}
AuthScheme authscheme = authState.getAuthScheme();
String id = authscheme.getSchemeName();
String id = authScheme.getSchemeName();
Header challenge = (Header) challenges.get(id.toLowerCase());
if (challenge == null) {
throw new AuthenticationException(id +
" authorization challenge expected, but not found");
}
authscheme.processChallenge(challenge);
authScheme.processChallenge(challenge);
LOG.debug("Authorization challenge processed");
}

View File

@ -249,8 +249,13 @@ public class DefaultHttpClient extends AbstractHttpClient {
}
protected AuthenticationHandler createAuthenticationHandler() {
return new DefaultAuthenticationHandler();
protected AuthenticationHandler createTargetAuthenticationHandler() {
return new DefaultTargetAuthenticationHandler();
}
protected AuthenticationHandler createProxyAuthenticationHandler() {
return new DefaultProxyAuthenticationHandler();
}

View File

@ -0,0 +1,72 @@
/*
* $HeadURL$
* $Revision$
* $Date$
*
* ====================================================================
* 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.Map;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AUTH;
import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.protocol.HttpContext;
/**
* @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
*/
public class DefaultProxyAuthenticationHandler extends AbstractAuthenticationHandler {
public DefaultProxyAuthenticationHandler() {
super();
}
public boolean isAuthenticationRequested(
final HttpResponse response,
final HttpContext context) {
if (response == null) {
throw new IllegalArgumentException("HTTP response may not be null");
}
int status = response.getStatusLine().getStatusCode();
return status == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED;
}
public Map getChallenges(
final HttpResponse response,
final HttpContext context) throws MalformedChallengeException {
if (response == null) {
throw new IllegalArgumentException("HTTP response may not be null");
}
Header[] headers = response.getHeaders(AUTH.PROXY_AUTH);
return parseChallenges(headers);
}
}

View File

@ -0,0 +1,72 @@
/*
* $HeadURL$
* $Revision$
* $Date$
*
* ====================================================================
* 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.Map;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AUTH;
import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.protocol.HttpContext;
/**
* @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
*/
public class DefaultTargetAuthenticationHandler extends AbstractAuthenticationHandler {
public DefaultTargetAuthenticationHandler() {
super();
}
public boolean isAuthenticationRequested(
final HttpResponse response,
final HttpContext context) {
if (response == null) {
throw new IllegalArgumentException("HTTP response may not be null");
}
int status = response.getStatusLine().getStatusCode();
return status == HttpStatus.SC_UNAUTHORIZED;
}
public Map getChallenges(
final HttpResponse response,
final HttpContext context) throws MalformedChallengeException {
if (response == null) {
throw new IllegalArgumentException("HTTP response may not be null");
}
Header[] headers = response.getHeaders(AUTH.WWW_AUTH);
return parseChallenges(headers);
}
}