HTTPCLIENT-1107: support for multiple auth options (first cut)

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1177782 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2011-09-30 19:16:15 +00:00
parent c514ac2a4c
commit 0cad2defa7
14 changed files with 562 additions and 209 deletions

View File

@ -28,6 +28,6 @@
public enum AuthProtocolState {
UNCHALLENGED, CHALLENGED, FAILURE, SUCCESS
UNCHALLENGED, CHALLENGED, HANDSHAKE, FAILURE, SUCCESS
}

View File

@ -26,6 +26,8 @@
package org.apache.http.auth;
import java.util.Queue;
import org.apache.http.annotation.NotThreadSafe;
@ -49,6 +51,8 @@ public class AuthState {
/** Credentials selected for authentication */
private Credentials credentials;
private Queue<AuthOption> authOptions;
/**
* Default constructor.
*
@ -151,6 +155,24 @@ public void setAuthScope(final AuthScope authScope) {
this.authScope = authScope;
}
/**
* Returns available authentication options.
*
* @return authentication options, if available, <code>null</null> otherwise.
*/
public Queue<AuthOption> getAuthOptions() {
return this.authOptions;
}
/**
* Sets authentication options to select from when authenticating.
*
* @param authOptions authentication options
*/
public void setAuthOptions(final Queue<AuthOption> authOptions) {
this.authOptions = authOptions != null && !authOptions.isEmpty() ? authOptions : null;
}
@Override
public String toString() {
StringBuilder buffer = new StringBuilder();

View File

@ -28,6 +28,7 @@
package org.apache.http.client;
import java.util.Map;
import java.util.Queue;
import org.apache.http.Header;
import org.apache.http.HttpHost;
@ -84,11 +85,11 @@ Map<String, Header> getChallenges(
* @param challenges collection of challenges.
* @param response HTTP response.
* @param context HTTP context.
* @return authentication scheme to use for authentication.
* @return authentication auth schemes that can be used for authentication. Can be empty.
* @throws MalformedChallengeException if one of the authentication
* challenges is not valid or malformed.
*/
AuthOption select(
Queue<AuthOption> select(
Map<String, Header> challenges,
HttpHost authhost,
HttpResponse response,

View File

@ -0,0 +1,136 @@
/*
* ====================================================================
* 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 java.util.Queue;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.AuthOption;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ContextAwareAuthScheme;
import org.apache.http.auth.Credentials;
import org.apache.http.protocol.HttpContext;
abstract class RequestAuthenticationBase implements HttpRequestInterceptor {
final Log log = LogFactory.getLog(getClass());
public RequestAuthenticationBase() {
super();
}
void process(
final AuthState authState,
final HttpRequest request,
final HttpContext context) throws HttpException, IOException {
AuthScheme authScheme = authState.getAuthScheme();
Credentials creds = authState.getCredentials();
if (this.log.isDebugEnabled()) {
this.log.debug("Authentication protocol state: " + authState.getState());
}
switch (authState.getState()) {
case FAILURE:
return;
case SUCCESS:
ensureAuthScheme(authScheme);
if (authScheme.isConnectionBased()) {
return;
}
break;
case CHALLENGED:
Queue<AuthOption> authOptions = authState.getAuthOptions();
if (authOptions != null) {
while (!authOptions.isEmpty()) {
AuthOption authOption = authOptions.remove();
authScheme = authOption.getAuthScheme();
creds = authOption.getCredentials();
authState.setAuthScheme(authScheme);
authState.setCredentials(creds);
if (this.log.isDebugEnabled()) {
this.log.debug("Generating response to an authentication challenge using "
+ authScheme.getSchemeName() + " scheme");
}
try {
Header header = authenticate(authScheme, creds, request, context);
request.addHeader(header);
authState.setAuthOptions(null);
break;
} catch (AuthenticationException ex) {
if (this.log.isWarnEnabled()) {
this.log.warn("Authentication error: " + ex.getMessage());
}
}
}
return;
} else {
ensureAuthScheme(authScheme);
}
}
if (authScheme != null) {
try {
Header header = authenticate(authScheme, creds, request, context);
request.addHeader(header);
} catch (AuthenticationException ex) {
if (this.log.isErrorEnabled()) {
this.log.error("Authentication error: " + ex.getMessage());
}
}
}
}
private void ensureAuthScheme(final AuthScheme authScheme) {
if (authScheme == null) {
throw new IllegalStateException("Auth scheme is not set");
}
}
@SuppressWarnings("deprecation")
private Header authenticate(
final AuthScheme authScheme,
final Credentials creds,
final HttpRequest request,
final HttpContext context) throws AuthenticationException {
if (authScheme == null) {
throw new IllegalStateException("Auth state object is null");
}
if (authScheme instanceof ContextAwareAuthScheme) {
return ((ContextAwareAuthScheme) authScheme).authenticate(creds, request, context);
} else {
return authScheme.authenticate(creds, request);
}
}
}

View File

@ -29,20 +29,11 @@
import java.io.IOException;
import org.apache.http.annotation.Immutable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.annotation.Immutable;
import org.apache.http.auth.AUTH;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ContextAwareAuthScheme;
import org.apache.http.auth.Credentials;
import org.apache.http.conn.HttpRoutedConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.protocol.ExecutionContext;
@ -55,15 +46,12 @@
* @since 4.0
*/
@Immutable
public class RequestProxyAuthentication implements HttpRequestInterceptor {
private final Log log = LogFactory.getLog(getClass());
public class RequestProxyAuthentication extends RequestAuthenticationBase {
public RequestProxyAuthentication() {
super();
}
@SuppressWarnings("deprecation")
public void process(final HttpRequest request, final HttpContext context)
throws HttpException, IOException {
if (request == null) {
@ -95,33 +83,7 @@ public void process(final HttpRequest request, final HttpContext context)
this.log.debug("Proxy auth state not set in the context");
return;
}
AuthScheme authScheme = authState.getAuthScheme();
if (authScheme == null) {
return;
}
Credentials creds = authState.getCredentials();
if (creds == null) {
this.log.debug("User credentials not available");
return;
}
if (authState.getAuthScope() != null || !authScheme.isConnectionBased()) {
try {
Header header;
if (authScheme instanceof ContextAwareAuthScheme) {
header = ((ContextAwareAuthScheme) authScheme).authenticate(
creds, request, context);
} else {
header = authScheme.authenticate(creds, request);
}
request.addHeader(header);
} catch (AuthenticationException ex) {
if (this.log.isErrorEnabled()) {
this.log.error("Proxy authentication error: " + ex.getMessage());
}
}
}
process(authState, request, context);
}
}

View File

@ -29,20 +29,11 @@
import java.io.IOException;
import org.apache.http.annotation.Immutable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.annotation.Immutable;
import org.apache.http.auth.AUTH;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ContextAwareAuthScheme;
import org.apache.http.auth.Credentials;
import org.apache.http.protocol.HttpContext;
/**
@ -52,15 +43,12 @@
* @since 4.0
*/
@Immutable
public class RequestTargetAuthentication implements HttpRequestInterceptor {
private final Log log = LogFactory.getLog(getClass());
public class RequestTargetAuthentication extends RequestAuthenticationBase {
public RequestTargetAuthentication() {
super();
}
@SuppressWarnings("deprecation")
public void process(final HttpRequest request, final HttpContext context)
throws HttpException, IOException {
if (request == null) {
@ -86,34 +74,7 @@ public void process(final HttpRequest request, final HttpContext context)
this.log.debug("Target auth state not set in the context");
return;
}
AuthScheme authScheme = authState.getAuthScheme();
if (authScheme == null) {
return;
}
Credentials creds = authState.getCredentials();
if (creds == null) {
this.log.debug("User credentials not available");
return;
}
if (authState.getAuthScope() != null || !authScheme.isConnectionBased()) {
try {
Header header;
if (authScheme instanceof ContextAwareAuthScheme) {
header = ((ContextAwareAuthScheme) authScheme).authenticate(
creds, request, context);
} else {
header = authScheme.authenticate(creds, request);
}
request.addHeader(header);
} catch (AuthenticationException ex) {
if (this.log.isErrorEnabled()) {
this.log.error("Authentication error: " + ex.getMessage());
}
}
}
process(authState, request, context);
}
}

View File

@ -45,6 +45,7 @@
import org.apache.http.annotation.ThreadSafe;
import org.apache.http.auth.AuthSchemeRegistry;
import org.apache.http.client.AuthenticationHandler;
import org.apache.http.client.AuthenticationStrategy;
import org.apache.http.client.BackoffManager;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ConnectionBackoffStrategy;
@ -140,13 +141,13 @@
* a collection user credentials. The {@link #createCredentialsProvider()}
* must be implemented by concrete super classes to instantiate
* this object.
* <li>{@link AuthenticationHandler}</li> object used to authenticate
* <li>{@link AuthenticationStrategy}</li> object used to authenticate
* against the target host.
* The {@link #createTargetAuthenticationHandler()} must be implemented
* The {@link #createTargetAuthenticationStrategy()} must be implemented
* by concrete super classes to instantiate this object.
* <li>{@link AuthenticationHandler}</li> object used to authenticate
* <li>{@link AuthenticationStrategy}</li> object used to authenticate
* against the proxy host.
* The {@link #createProxyAuthenticationHandler()} must be implemented
* The {@link #createProxyAuthenticationStrategy()} must be implemented
* by concrete super classes to instantiate this object.
* <li>{@link HttpRoutePlanner}</li> object used to calculate a route
* for establishing a connection to the target host. The route
@ -229,11 +230,11 @@ public abstract class AbstractHttpClient implements HttpClient {
/** The target authentication handler. */
@GuardedBy("this")
private AuthenticationHandler targetAuthHandler;
private AuthenticationStrategy targetAuthStrategy;
/** The proxy authentication handler. */
@GuardedBy("this")
private AuthenticationHandler proxyAuthHandler;
private AuthenticationStrategy proxyAuthStrategy;
/** The cookie store. */
@GuardedBy("this")
@ -399,11 +400,23 @@ protected RedirectHandler createRedirectHandler() {
}
protected AuthenticationStrategy createTargetAuthenticationStrategy() {
return new TargetAuthenticationStrategy();
}
@Deprecated
protected AuthenticationHandler createTargetAuthenticationHandler() {
return new DefaultTargetAuthenticationHandler();
}
protected AuthenticationStrategy createProxyAuthenticationStrategy() {
return new ProxyAuthenticationStrategy();
}
@Deprecated
protected AuthenticationHandler createProxyAuthenticationHandler() {
return new DefaultProxyAuthenticationHandler();
}
@ -472,8 +485,8 @@ public synchronized final AuthSchemeRegistry getAuthSchemes() {
return supportedAuthSchemes;
}
public synchronized void setAuthSchemes(final AuthSchemeRegistry authSchemeRegistry) {
supportedAuthSchemes = authSchemeRegistry;
public synchronized void setAuthSchemes(final AuthSchemeRegistry registry) {
supportedAuthSchemes = registry;
}
public synchronized final ConnectionBackoffStrategy getConnectionBackoffStrategy() {
@ -499,8 +512,8 @@ public synchronized void setBackoffManager(final BackoffManager manager) {
backoffManager = manager;
}
public synchronized void setCookieSpecs(final CookieSpecRegistry cookieSpecRegistry) {
supportedCookieSpecs = cookieSpecRegistry;
public synchronized void setCookieSpecs(final CookieSpecRegistry registry) {
supportedCookieSpecs = registry;
}
public synchronized final ConnectionReuseStrategy getConnectionReuseStrategy() {
@ -511,8 +524,8 @@ public synchronized final ConnectionReuseStrategy getConnectionReuseStrategy() {
}
public synchronized void setReuseStrategy(final ConnectionReuseStrategy reuseStrategy) {
this.reuseStrategy = reuseStrategy;
public synchronized void setReuseStrategy(final ConnectionReuseStrategy strategy) {
this.reuseStrategy = strategy;
}
@ -524,8 +537,8 @@ public synchronized final ConnectionKeepAliveStrategy getConnectionKeepAliveStra
}
public synchronized void setKeepAliveStrategy(final ConnectionKeepAliveStrategy keepAliveStrategy) {
this.keepAliveStrategy = keepAliveStrategy;
public synchronized void setKeepAliveStrategy(final ConnectionKeepAliveStrategy strategy) {
this.keepAliveStrategy = strategy;
}
@ -537,8 +550,8 @@ public synchronized final HttpRequestRetryHandler getHttpRequestRetryHandler() {
}
public synchronized void setHttpRequestRetryHandler(final HttpRequestRetryHandler retryHandler) {
this.retryHandler = retryHandler;
public synchronized void setHttpRequestRetryHandler(final HttpRequestRetryHandler handler) {
this.retryHandler = handler;
}
@ -549,8 +562,8 @@ public synchronized final RedirectHandler getRedirectHandler() {
@Deprecated
public synchronized void setRedirectHandler(final RedirectHandler redirectHandler) {
this.redirectStrategy = new DefaultRedirectStrategyAdaptor(redirectHandler);
public synchronized void setRedirectHandler(final RedirectHandler handler) {
this.redirectStrategy = new DefaultRedirectStrategyAdaptor(handler);
}
/**
@ -566,36 +579,70 @@ public synchronized final RedirectStrategy getRedirectStrategy() {
/**
* @since 4.1
*/
public synchronized void setRedirectStrategy(final RedirectStrategy redirectStrategy) {
this.redirectStrategy = redirectStrategy;
public synchronized void setRedirectStrategy(final RedirectStrategy strategy) {
this.redirectStrategy = strategy;
}
@Deprecated
public synchronized final AuthenticationHandler getTargetAuthenticationHandler() {
if (targetAuthHandler == null) {
targetAuthHandler = createTargetAuthenticationHandler();
return createTargetAuthenticationHandler();
}
@Deprecated
public synchronized void setTargetAuthenticationHandler(final AuthenticationHandler handler) {
this.targetAuthStrategy = new AuthenticationStrategyAdaptor(handler);
}
/**
* @since 4.2
*/
public synchronized final AuthenticationStrategy getTargetAuthenticationStrategy() {
if (targetAuthStrategy == null) {
targetAuthStrategy = createTargetAuthenticationStrategy();
}
return targetAuthHandler;
return targetAuthStrategy;
}
public synchronized void setTargetAuthenticationHandler(
final AuthenticationHandler targetAuthHandler) {
this.targetAuthHandler = targetAuthHandler;
/**
* @since 4.2
*/
public synchronized void setTargetAuthenticationStrategy(final AuthenticationStrategy strategy) {
this.targetAuthStrategy = strategy;
}
@Deprecated
public synchronized final AuthenticationHandler getProxyAuthenticationHandler() {
if (proxyAuthHandler == null) {
proxyAuthHandler = createProxyAuthenticationHandler();
}
return proxyAuthHandler;
return createProxyAuthenticationHandler();
}
public synchronized void setProxyAuthenticationHandler(
final AuthenticationHandler proxyAuthHandler) {
this.proxyAuthHandler = proxyAuthHandler;
@Deprecated
public synchronized void setProxyAuthenticationHandler(final AuthenticationHandler handler) {
this.proxyAuthStrategy = new AuthenticationStrategyAdaptor(handler);
}
/**
* @since 4.2
*/
public synchronized final AuthenticationStrategy getProxyAuthenticationStrategy() {
if (proxyAuthStrategy == null) {
proxyAuthStrategy = createProxyAuthenticationStrategy();
}
return proxyAuthStrategy;
}
/**
* @since 4.2
*/
public synchronized void setProxyAuthenticationStrategy(final AuthenticationStrategy strategy) {
this.proxyAuthStrategy = strategy;
}
@ -646,8 +693,8 @@ public synchronized final UserTokenHandler getUserTokenHandler() {
}
public synchronized void setUserTokenHandler(final UserTokenHandler userTokenHandler) {
this.userTokenHandler = userTokenHandler;
public synchronized void setUserTokenHandler(final UserTokenHandler handler) {
this.userTokenHandler = handler;
}
@ -834,8 +881,8 @@ public final HttpResponse execute(HttpHost target, HttpRequest request,
getProtocolProcessor(),
getHttpRequestRetryHandler(),
getRedirectStrategy(),
getTargetAuthenticationHandler(),
getProxyAuthenticationHandler(),
getTargetAuthenticationStrategy(),
getProxyAuthenticationStrategy(),
getUserTokenHandler(),
determineParams(request));
routePlanner = getRoutePlanner();
@ -889,10 +936,10 @@ protected RequestDirector createClientRequestDirector(
final HttpRoutePlanner rouplan,
final HttpProcessor httpProcessor,
final HttpRequestRetryHandler retryHandler,
final org.apache.http.client.RedirectHandler redirectHandler,
final RedirectHandler redirectHandler,
final AuthenticationHandler targetAuthHandler,
final AuthenticationHandler proxyAuthHandler,
final UserTokenHandler stateHandler,
final UserTokenHandler userTokenHandler,
final HttpParams params) {
return new DefaultRequestDirector(
requestExec,
@ -905,13 +952,11 @@ protected RequestDirector createClientRequestDirector(
redirectHandler,
targetAuthHandler,
proxyAuthHandler,
stateHandler,
userTokenHandler,
params);
}
/**
* @since 4.1
*/
@Deprecated
protected RequestDirector createClientRequestDirector(
final HttpRequestExecutor requestExec,
final ClientConnectionManager conman,
@ -923,7 +968,7 @@ protected RequestDirector createClientRequestDirector(
final RedirectStrategy redirectStrategy,
final AuthenticationHandler targetAuthHandler,
final AuthenticationHandler proxyAuthHandler,
final UserTokenHandler stateHandler,
final UserTokenHandler userTokenHandler,
final HttpParams params) {
return new DefaultRequestDirector(
log,
@ -937,9 +982,43 @@ protected RequestDirector createClientRequestDirector(
redirectStrategy,
targetAuthHandler,
proxyAuthHandler,
stateHandler,
userTokenHandler,
params);
}
/**
* @since 4.2
*/
protected RequestDirector createClientRequestDirector(
final HttpRequestExecutor requestExec,
final ClientConnectionManager conman,
final ConnectionReuseStrategy reustrat,
final ConnectionKeepAliveStrategy kastrat,
final HttpRoutePlanner rouplan,
final HttpProcessor httpProcessor,
final HttpRequestRetryHandler retryHandler,
final RedirectStrategy redirectStrategy,
final AuthenticationStrategy targetAuthStrategy,
final AuthenticationStrategy proxyAuthStrategy,
final UserTokenHandler userTokenHandler,
final HttpParams params) {
return new DefaultRequestDirector(
log,
requestExec,
conman,
reustrat,
kastrat,
rouplan,
httpProcessor,
retryHandler,
redirectStrategy,
targetAuthStrategy,
proxyAuthStrategy,
userTokenHandler,
params);
}
/**
* Obtains parameters for executing a request.
* The default implementation in this class creates a new

View File

@ -27,8 +27,10 @@
package org.apache.http.impl.client;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
import java.util.Queue;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -76,16 +78,30 @@ public Map<String, Header> getChallenges(
return this.handler.getChallenges(response, context);
}
public AuthOption select(
public Queue<AuthOption> select(
final Map<String, Header> challenges,
final HttpHost authhost,
final HttpResponse response,
final HttpContext context) throws MalformedChallengeException {
if (challenges == null) {
throw new IllegalArgumentException("Map of auth challenges may not be null");
}
if (authhost == null) {
throw new IllegalArgumentException("Host may not be null");
}
if (response == null) {
throw new IllegalArgumentException("HTTP response may not be null");
}
if (context == null) {
throw new IllegalArgumentException("HTTP context may not be null");
}
Queue<AuthOption> options = new LinkedList<AuthOption>();
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
ClientContext.CREDS_PROVIDER);
if (credsProvider == null) {
this.log.debug("Credentials provider not set in the context");
return null;
return options;
}
AuthScheme authScheme;
@ -95,7 +111,7 @@ public AuthOption select(
if (this.log.isWarnEnabled()) {
this.log.warn(ex.getMessage(), ex);
}
return null;
return options;
}
String id = authScheme.getSchemeName();
Header challenge = challenges.get(id.toLowerCase(Locale.US));
@ -109,10 +125,9 @@ public AuthOption select(
Credentials credentials = credsProvider.getCredentials(authScope);
if (credentials != null) {
return new AuthOption(authScheme, credentials);
} else {
return null;
options.add(new AuthOption(authScheme, credentials));
}
return options;
}
public AuthenticationHandler getHandler() {

View File

@ -30,9 +30,11 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Queue;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -124,7 +126,7 @@ public Map<String, Header> getChallenges(
return map;
}
public AuthOption select(
public Queue<AuthOption> select(
final Map<String, Header> challenges,
final HttpHost authhost,
final HttpResponse response,
@ -142,17 +144,18 @@ public AuthOption select(
throw new IllegalArgumentException("HTTP context may not be null");
}
Queue<AuthOption> options = new LinkedList<AuthOption>();
AuthSchemeRegistry registry = (AuthSchemeRegistry) context.getAttribute(
ClientContext.AUTHSCHEME_REGISTRY);
if (registry == null) {
this.log.debug("Auth scheme registry not set in the context");
return null;
return options;
}
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
ClientContext.CREDS_PROVIDER);
if (credsProvider == null) {
this.log.debug("Credentials provider not set in the context");
return null;
return options;
}
@SuppressWarnings("unchecked")
@ -179,7 +182,7 @@ public AuthOption select(
Credentials credentials = credsProvider.getCredentials(authScope);
if (credentials != null) {
return new AuthOption(authScheme, credentials);
options.add(new AuthOption(authScheme, credentials));
}
} catch (IllegalStateException e) {
if (this.log.isWarnEnabled()) {
@ -194,7 +197,7 @@ public AuthOption select(
}
}
}
return null;
return options;
}
}

View File

@ -29,6 +29,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Queue;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -64,9 +65,12 @@ public boolean isAuthenticationRequested(
if (authStrategy.isAuthenticationRequested(response, context)) {
return true;
} else {
if (authState.getState() == AuthProtocolState.CHALLENGED) {
switch (authState.getState()) {
case CHALLENGED:
case HANDSHAKE:
authState.setState(AuthProtocolState.SUCCESS);
} else {
break;
default:
authState.setState(AuthProtocolState.UNCHALLENGED);
}
return false;
@ -101,7 +105,7 @@ public boolean authenticate(
authState.setCredentials(null);
return false;
} else {
authState.setState(AuthProtocolState.CHALLENGED);
authState.setState(AuthProtocolState.HANDSHAKE);
return true;
}
} else {
@ -109,14 +113,14 @@ public boolean authenticate(
// Retry authentication with a different scheme
}
}
AuthOption authOption = authStrategy.select(challenges, host, response, context);
if (authOption == null) {
Queue<AuthOption> authOptions = authStrategy.select(challenges, host, response, context);
authState.setState(AuthProtocolState.CHALLENGED);
if (authOptions != null && !authOptions.isEmpty()) {
authState.setAuthOptions(authOptions);
return true;
} else {
return false;
}
authState.setAuthScheme(authOption.getAuthScheme());
authState.setCredentials(authOption.getCredentials());
authState.setState(AuthProtocolState.CHALLENGED);
return true;
} catch (MalformedChallengeException ex) {
if (this.log.isWarnEnabled()) {
this.log.warn("Malformed challenge: " + ex.getMessage());

View File

@ -0,0 +1,221 @@
/*
* ====================================================================
*
* 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 java.util.LinkedList;
import junit.framework.Assert;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.AUTH;
import org.apache.http.auth.AuthOption;
import org.apache.http.auth.AuthProtocolState;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ContextAwareAuthScheme;
import org.apache.http.auth.Credentials;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class TestRequestAuthenticationBase {
static class TestRequestAuthentication extends RequestAuthenticationBase {
public void process(
final HttpRequest request,
final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute("test-auth-state");
super.process(authState, request, context);
}
}
private ContextAwareAuthScheme authScheme;
private Credentials credentials;
private AuthState authState;
private HttpContext context;
private HttpRequestInterceptor interceptor;
@Before
public void setUp() throws Exception {
this.authScheme = Mockito.mock(ContextAwareAuthScheme.class);
this.credentials = Mockito.mock(Credentials.class);
this.authState = new AuthState();
this.context = new BasicHttpContext();
this.context.setAttribute("test-auth-state", this.authState);
this.interceptor = new TestRequestAuthentication();
}
@Test
public void testAuthFailureState() throws Exception {
HttpRequest request = new BasicHttpRequest("GET", "/");
this.authState.setState(AuthProtocolState.FAILURE);
this.authState.setAuthScheme(this.authScheme);
this.interceptor.process(request, this.context);
Assert.assertFalse(request.containsHeader(AUTH.WWW_AUTH_RESP));
Mockito.verify(this.authScheme, Mockito.never()).authenticate(
Mockito.any(Credentials.class),
Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class));
}
@Test
public void testAuthChallengeStateNoOption() throws Exception {
HttpRequest request = new BasicHttpRequest("GET", "/");
this.authState.setState(AuthProtocolState.CHALLENGED);
this.authState.setAuthScheme(this.authScheme);
this.authState.setCredentials(this.credentials);
Mockito.when(this.authScheme.authenticate(
Mockito.any(Credentials.class),
Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(AUTH.WWW_AUTH_RESP, "stuff"));
this.interceptor.process(request, this.context);
Assert.assertTrue(request.containsHeader(AUTH.WWW_AUTH_RESP));
Mockito.verify(this.authScheme).authenticate(this.credentials, request, this.context);
}
@Test
public void testAuthChallengeStateOneOptions() throws Exception {
HttpRequest request = new BasicHttpRequest("GET", "/");
this.authState.setState(AuthProtocolState.CHALLENGED);
LinkedList<AuthOption> authOptions = new LinkedList<AuthOption>();
authOptions.add(new AuthOption(this.authScheme, this.credentials));
this.authState.setAuthOptions(authOptions);
Mockito.when(this.authScheme.authenticate(
Mockito.any(Credentials.class),
Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(AUTH.WWW_AUTH_RESP, "stuff"));
this.interceptor.process(request, this.context);
Assert.assertSame(this.authScheme, this.authState.getAuthScheme());
Assert.assertSame(this.credentials, this.authState.getCredentials());
Assert.assertNull(this.authState.getAuthOptions());
Assert.assertTrue(request.containsHeader(AUTH.WWW_AUTH_RESP));
Mockito.verify(this.authScheme).authenticate(this.credentials, request, this.context);
}
@Test
public void testAuthChallengeStateMultipleOption() throws Exception {
HttpRequest request = new BasicHttpRequest("GET", "/");
this.authState.setState(AuthProtocolState.CHALLENGED);
LinkedList<AuthOption> authOptions = new LinkedList<AuthOption>();
ContextAwareAuthScheme authScheme1 = Mockito.mock(ContextAwareAuthScheme.class);
Mockito.doThrow(new AuthenticationException()).when(authScheme1).authenticate(
Mockito.any(Credentials.class),
Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class));
ContextAwareAuthScheme authScheme2 = Mockito.mock(ContextAwareAuthScheme.class);
Mockito.when(authScheme2.authenticate(
Mockito.any(Credentials.class),
Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(AUTH.WWW_AUTH_RESP, "stuff"));
authOptions.add(new AuthOption(authScheme1, this.credentials));
authOptions.add(new AuthOption(authScheme2, this.credentials));
this.authState.setAuthOptions(authOptions);
this.interceptor.process(request, this.context);
Assert.assertSame(authScheme2, this.authState.getAuthScheme());
Assert.assertSame(this.credentials, this.authState.getCredentials());
Assert.assertNull(this.authState.getAuthOptions());
Assert.assertTrue(request.containsHeader(AUTH.WWW_AUTH_RESP));
Mockito.verify(authScheme1, Mockito.times(1)).authenticate(this.credentials, request, this.context);
Mockito.verify(authScheme2, Mockito.times(1)).authenticate(this.credentials, request, this.context);
}
@Test
public void testAuthSuccess() throws Exception {
HttpRequest request = new BasicHttpRequest("GET", "/");
this.authState.setState(AuthProtocolState.SUCCESS);
this.authState.setAuthScheme(this.authScheme);
this.authState.setCredentials(this.credentials);
Mockito.when(this.authScheme.isConnectionBased()).thenReturn(Boolean.FALSE);
Mockito.when(this.authScheme.authenticate(
Mockito.any(Credentials.class),
Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(AUTH.WWW_AUTH_RESP, "stuff"));
this.interceptor.process(request, this.context);
Assert.assertSame(this.authScheme, this.authState.getAuthScheme());
Assert.assertSame(this.credentials, this.authState.getCredentials());
Assert.assertNull(this.authState.getAuthOptions());
Assert.assertTrue(request.containsHeader(AUTH.WWW_AUTH_RESP));
Mockito.verify(this.authScheme).authenticate(this.credentials, request, this.context);
}
@Test
public void testAuthSuccessConnectionBased() throws Exception {
HttpRequest request = new BasicHttpRequest("GET", "/");
this.authState.setState(AuthProtocolState.SUCCESS);
this.authState.setAuthScheme(this.authScheme);
this.authState.setCredentials(this.credentials);
Mockito.when(this.authScheme.isConnectionBased()).thenReturn(Boolean.TRUE);
Mockito.when(this.authScheme.authenticate(
Mockito.any(Credentials.class),
Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(AUTH.WWW_AUTH_RESP, "stuff"));
this.interceptor.process(request, this.context);
Assert.assertFalse(request.containsHeader(AUTH.WWW_AUTH_RESP));
Mockito.verify(this.authScheme, Mockito.never()).authenticate(
Mockito.any(Credentials.class),
Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class));
}
}

View File

@ -210,36 +210,6 @@ public void testAuthSchemeNotSet() throws Exception {
Assert.assertNull(header);
}
@Test
public void testAuthCredentialsNotSet() throws Exception {
HttpRequest request = new BasicHttpRequest("GET", "/");
HttpContext context = new BasicHttpContext();
HttpHost target = new HttpHost("localhost", 80, "http");
HttpHost proxy = new HttpHost("localhost", 8080);
HttpRoute route = new HttpRoute(target, null, proxy, false,
TunnelType.PLAIN, LayerType.PLAIN);
HttpRoutedConnection conn = Mockito.mock(HttpRoutedConnection.class);
Mockito.when(conn.getRoute()).thenReturn(route);
AuthState authstate = new AuthState();
BasicScheme authscheme = new BasicScheme();
BasicHeader challenge = new BasicHeader(AUTH.PROXY_AUTH, "BASIC realm=auth-realm");
authscheme.processChallenge(challenge);
authstate.setAuthScheme(authscheme);
context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
context.setAttribute(ClientContext.PROXY_AUTH_STATE, authstate);
HttpRequestInterceptor interceptor = new RequestProxyAuthentication();
interceptor.process(request, context);
Header header = request.getFirstHeader(AUTH.PROXY_AUTH_RESP);
Assert.assertNull(header);
}
@Test
public void testConnectionBasedAuthOnlyIfChallenged() throws Exception {
HttpRequest request = new BasicHttpRequest("GET", "/");
@ -271,7 +241,7 @@ public boolean isConnectionBased() {
authstate.setAuthScheme(authscheme);
authstate.setCredentials(creds);
authstate.setState(AuthProtocolState.UNCHALLENGED);
authstate.setState(AuthProtocolState.SUCCESS);
context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
context.setAttribute(ClientContext.PROXY_AUTH_STATE, authstate);

View File

@ -157,27 +157,6 @@ public void testAuthSchemeNotSet() throws Exception {
Assert.assertNull(header);
}
@Test
public void testAuthCredentialsNotSet() throws Exception {
HttpRequest request = new BasicHttpRequest("GET", "/");
HttpContext context = new BasicHttpContext();
AuthState authstate = new AuthState();
BasicScheme authscheme = new BasicScheme();
BasicHeader challenge = new BasicHeader(AUTH.WWW_AUTH, "BASIC realm=auth-realm");
authscheme.processChallenge(challenge);
authstate.setAuthScheme(authscheme);
context.setAttribute(ClientContext.TARGET_AUTH_STATE, authstate);
HttpRequestInterceptor interceptor = new RequestTargetAuthentication();
interceptor.process(request, context);
Header header = request.getFirstHeader(AUTH.WWW_AUTH_RESP);
Assert.assertNull(header);
}
@Test
public void testConnectionBasedAuthOnlyIfChallenged() throws Exception {
HttpRequest request = new BasicHttpRequest("GET", "/");
@ -201,7 +180,7 @@ public boolean isConnectionBased() {
authstate.setAuthScheme(authscheme);
authstate.setCredentials(creds);
authstate.setState(AuthProtocolState.UNCHALLENGED);
authstate.setState(AuthProtocolState.SUCCESS);
context.setAttribute(ClientContext.TARGET_AUTH_STATE, authstate);

View File

@ -333,11 +333,11 @@ public void testBasicAuthenticationFailureOnNonRepeatablePost() throws Exception
}
}
static class TestTargetAuthenticationHandler extends DefaultTargetAuthenticationHandler {
static class TestTargetAuthenticationStrategy extends TargetAuthenticationStrategy {
private int count;
public TestTargetAuthenticationHandler() {
public TestTargetAuthenticationStrategy() {
super();
this.count = 0;
}
@ -372,10 +372,10 @@ public void testBasicAuthenticationCredentialsCaching() throws Exception {
credsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials("test", "test"));
TestTargetAuthenticationHandler authHandler = new TestTargetAuthenticationHandler();
TestTargetAuthenticationStrategy authStrategy = new TestTargetAuthenticationStrategy();
this.httpclient.setCredentialsProvider(credsProvider);
this.httpclient.setTargetAuthenticationHandler(authHandler);
this.httpclient.setTargetAuthenticationStrategy(authStrategy);
HttpContext context = new BasicHttpContext();
@ -393,7 +393,7 @@ public void testBasicAuthenticationCredentialsCaching() throws Exception {
Assert.assertNotNull(entity2);
EntityUtils.consume(entity2);
Assert.assertEquals(1, authHandler.getCount());
Assert.assertEquals(1, authStrategy.getCount());
}
}