diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index f0529015a..972ac3944 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -1,6 +1,10 @@ Changes since 4.0 ------------------- +* Added parameters to define the order of preference for supported auth + schemes for target host and proxy authentication. + Contributed by Oleg Kalnichevski + * [HTTPCLIENT-875] DefaultClientConnectionOperator#openConnection doesn't update the connection state if the connection socket changed after the call to SocketFactory#connectSocket(). diff --git a/httpclient/src/main/java/org/apache/http/auth/params/AuthPNames.java b/httpclient/src/main/java/org/apache/http/auth/params/AuthPNames.java index 7a0137e88..142042d28 100644 --- a/httpclient/src/main/java/org/apache/http/auth/params/AuthPNames.java +++ b/httpclient/src/main/java/org/apache/http/auth/params/AuthPNames.java @@ -27,6 +27,8 @@ package org.apache.http.auth.params; +import org.apache.http.auth.AuthScheme; + /** * Parameter names for HTTP authentication classes. * @@ -42,4 +44,26 @@ public interface AuthPNames { */ public static final String CREDENTIAL_CHARSET = "http.auth.credential-charset"; + /** + * Defines the order of preference for supported {@link AuthScheme}s when + * authenticating with the target host. + *

+ * This parameter expects a value of type {@link java.util.Collection}. The + * collection is expected to contain {@link String} instances representing + * a name of an authentication scheme as returned by + * {@link AuthScheme#getSchemeName()}. + */ + public static final String TARGET_AUTH_PREF = "http.auth.target-scheme-pref"; + + /** + * Defines the order of preference for supported {@link AuthScheme}s when + * authenticating with the proxy host. + *

+ * This parameter expects a value of type {@link java.util.Collection}. The + * collection is expected to contain {@link String} instances representing + * a name of an authentication scheme as returned by + * {@link AuthScheme#getSchemeName()}. + */ + public static final String PROXY_AUTH_PREF = "http.auth.proxy-scheme-pref"; + } diff --git a/httpclient/src/main/java/org/apache/http/client/params/AuthPolicy.java b/httpclient/src/main/java/org/apache/http/client/params/AuthPolicy.java index 52f9f338d..c7405f0fe 100644 --- a/httpclient/src/main/java/org/apache/http/client/params/AuthPolicy.java +++ b/httpclient/src/main/java/org/apache/http/client/params/AuthPolicy.java @@ -59,4 +59,11 @@ public final class AuthPolicy { */ public static final String BASIC = "Basic"; + /** + * SPNEGO/Kerberos Authentication scheme. + * + * @since 4.1 + */ + public static final String SPNEGO = "negotiate"; + } diff --git a/httpclient/src/main/java/org/apache/http/client/protocol/ClientContext.java b/httpclient/src/main/java/org/apache/http/client/protocol/ClientContext.java index 5da41e208..ebc8815a9 100644 --- a/httpclient/src/main/java/org/apache/http/client/protocol/ClientContext.java +++ b/httpclient/src/main/java/org/apache/http/client/protocol/ClientContext.java @@ -89,8 +89,9 @@ public interface ClientContext { public static final String PROXY_AUTH_STATE = "http.auth.proxy-scope"; /** - * RESERVED. DO NOT USE!!! + * @deprecated do not use */ + @Deprecated public static final String AUTH_SCHEME_PREF = "http.auth.scheme-pref"; /** diff --git a/httpclient/src/main/java/org/apache/http/impl/client/AbstractAuthenticationHandler.java b/httpclient/src/main/java/org/apache/http/impl/client/AbstractAuthenticationHandler.java index 5140b2963..13772594d 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/AbstractAuthenticationHandler.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/AbstractAuthenticationHandler.java @@ -35,18 +35,18 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import org.apache.http.annotation.Immutable; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.FormattedHeader; import org.apache.http.Header; import org.apache.http.HttpResponse; +import org.apache.http.annotation.Immutable; 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.AuthPolicy; import org.apache.http.client.protocol.ClientContext; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; @@ -64,9 +64,10 @@ public abstract class AbstractAuthenticationHandler implements AuthenticationHan private static final List DEFAULT_SCHEME_PRIORITY = Collections.unmodifiableList(Arrays.asList(new String[] { - "ntlm", - "digest", - "basic" + AuthPolicy.SPNEGO, + AuthPolicy.NTLM, + AuthPolicy.DIGEST, + AuthPolicy.BASIC })); public AbstractAuthenticationHandler() { @@ -106,11 +107,31 @@ public abstract class AbstractAuthenticationHandler implements AuthenticationHan return map; } + /** + * Returns default list of auth scheme names in their order of preference. + * + * @return list of auth scheme names + */ protected List getAuthPreferences() { return DEFAULT_SCHEME_PRIORITY; } - public AuthScheme selectScheme( + /** + * Returns default list of auth scheme names in their order of preference + * based on the HTTP response and the current execution context. + * + * @param response HTTP response. + * @param context HTTP execution context. + * + * @since 4.1 + */ + protected List getAuthPreferences( + final HttpResponse response, + final HttpContext context) { + return getAuthPreferences(); + } + + public AuthScheme selectScheme( final Map challenges, final HttpResponse response, final HttpContext context) throws AuthenticationException { @@ -121,11 +142,9 @@ public abstract class AbstractAuthenticationHandler implements AuthenticationHan throw new IllegalStateException("AuthScheme registry not set in HTTP context"); } - @SuppressWarnings("unchecked") - Collection authPrefs = (Collection) context.getAttribute( - ClientContext.AUTH_SCHEME_PREF); + Collection authPrefs = getAuthPreferences(response, context); if (authPrefs == null) { - authPrefs = getAuthPreferences(); + authPrefs = DEFAULT_SCHEME_PRIORITY; } if (this.log.isDebugEnabled()) { diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java index 20a841706..cf929723f 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java @@ -27,6 +27,7 @@ package org.apache.http.impl.client; +import java.util.List; import java.util.Map; import org.apache.http.annotation.Immutable; @@ -36,6 +37,7 @@ 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.auth.params.AuthPNames; import org.apache.http.client.AuthenticationHandler; import org.apache.http.protocol.HttpContext; @@ -72,4 +74,18 @@ public class DefaultProxyAuthenticationHandler extends AbstractAuthenticationHan return parseChallenges(headers); } + @Override + protected List getAuthPreferences( + final HttpResponse response, + final HttpContext context) { + @SuppressWarnings("unchecked") + List authpref = (List) response.getParams().getParameter( + AuthPNames.PROXY_AUTH_PREF); + if (authpref != null) { + return authpref; + } else { + return super.getAuthPreferences(response, context); + } + } + } diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java index 2815d05e5..19ffd09a1 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java @@ -27,6 +27,7 @@ package org.apache.http.impl.client; +import java.util.List; import java.util.Map; import org.apache.http.annotation.Immutable; @@ -36,6 +37,7 @@ 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.auth.params.AuthPNames; import org.apache.http.client.AuthenticationHandler; import org.apache.http.protocol.HttpContext; @@ -72,4 +74,19 @@ public class DefaultTargetAuthenticationHandler extends AbstractAuthenticationHa return parseChallenges(headers); } + @Override + protected List getAuthPreferences( + final HttpResponse response, + final HttpContext context) { + @SuppressWarnings("unchecked") + List authpref = (List) response.getParams().getParameter( + AuthPNames.TARGET_AUTH_PREF); + if (authpref != null) { + return authpref; + } else { + return super.getAuthPreferences(response, context); + } + } + } + diff --git a/src/docbkx/authentication.xml b/src/docbkx/authentication.xml index be37a5296..5e718eb13 100644 --- a/src/docbkx/authentication.xml +++ b/src/docbkx/authentication.xml @@ -103,8 +103,8 @@ pwd those applications that do not want the overhead of full transport security through TLS/SSL encryption. - + NTLM: NTLM is a proprietary authentication scheme developed by Microsoft and @@ -130,8 +130,8 @@ pwd If this parameter is not set HttpClient will handle authentication automatically. - + 'http.auth.credential-charset': defines the charset to be used when encoding user credentials. This @@ -139,7 +139,41 @@ pwd this parameter is not set US-ASCII will be used. + + + 'http.auth.target-scheme-pref': + Defines the order of preference for supported + AuthSchemes when authenticating with the + target host. This parameter expects a value of type + java.util.Collection. The collection is expected + to contain java.lang.String instances representing a + name of an authentication scheme as returned by + AuthScheme#getSchemeName() + + + + + 'http.auth.proxy-scheme-pref': + Defines the order of preference for supported + AuthSchemes when authenticating with the + proxy host. This parameter expects a value of type + java.util.Collection. The collection is expected + to contain java.lang.String instances representing a + name of an authentication scheme as returned by + AuthScheme#getSchemeName() + + + For example, one can force HttpClient to use a different order of preference for + authentication schemes + authpref = new ArrayList(); +authpref.add(AuthPolicy.BASIC); +authpref.add(AuthPolicy.DIGEST); +httpclient.getParams().setParameter(AuthPNames.PROXY_AUTH_PREF, authpref); +]]>

Authentication scheme registry @@ -332,44 +366,59 @@ httpclient.addRequestInterceptor(preemptiveAuth, 0);
<literal>SPNEGO</literal>/Kerberos Authentication - SPNEGO (Simple and Protected GSSAPI -Negotiation Mechanism) is designed to allow for authentication -to services when neither end knows what the other can use/provide. It is most -commonly used to do Kerberos authentication. It can wrap other mechanisms, however -the current version in HTTPClient is designed solely with Kerberos in mind. The image below shows a simple authentication process. - + SPNEGO (Simple and + Protected GSSAPI + Negotiation Mechanism) is designed to allow for authentication to + services when neither end knows what the other can use/provide. It is most commonly used + to do Kerberos authentication. It can wrap other mechanisms, however the current version + in HTTPClient is designed solely with Kerberos in mind. The image below shows a simple + authentication process. - + - - 1. Client Web Browser does HTTP GET for resource. - 2. Web server returns HTTP 401 status and a header: "WWW-Authenticate: Negotiate - 3. Client generates a NegTokenInit, base64 encodes it, and resubmits the GET with an Authorization header: "Authorization: Negotiate <base64 encoding>". - 4. Server decodes the NegTokenInit, extracts the supported MechTypes (only Kerberos V5 in our case), ensures it is one of the expected ones, and then extracts the MechToken (Kerberos Token) and authenticates it. - 4a. If more processing is required another HTTP 401 is returned to the client with more data in the the WWW-Authenticate header. Client takes the info and generates another token passing this back in the Authorization header.... until complete. - 5. When the client has been authenticated the Web server should return the HTTP 200 status, a final WWW-Authenticate header and the page content. + + 1. Client Web Browser does HTTP GET for resource. + 2. Web server returns HTTP 401 status and a header: "WWW-Authenticate: + Negotiate + 3. Client generates a NegTokenInit, base64 encodes it, and resubmits the GET with + an Authorization header: "Authorization: Negotiate <base64 + encoding>". + 4. Server decodes the NegTokenInit, extracts the supported MechTypes (only + Kerberos V5 in our case), ensures it is one of the expected ones, and then extracts + the MechToken (Kerberos Token) and authenticates it. + 4a. If more processing is required another HTTP 401 is returned to the client with + more data in the the WWW-Authenticate header. Client takes the info and generates + another token passing this back in the Authorization header.... until + complete. + 5. When the client has been authenticated the Web server should return the HTTP + 200 status, a final WWW-Authenticate header and the page content.
HTTPClient Implementation Supports Sun Java versions 1.5 and up. - The JRE provides the supporting classes to do nearly all the kerberos and SPNEGO token handling. This means that a lot of the setup -is for the GSS classes. The NegotiateScheme is a simple class to handle marshalling the tokens and reading and writing the correct headers. + The JRE provides the supporting classes to do nearly all the kerberos and SPNEGO + token handling. This means that a lot of the setup is for the GSS classes. The + NegotiateScheme is a simple class to handle marshalling the tokens and reading and + writing the correct headers.
Usage. - The best way to start is to grab the KerberosHttpClient.java file in examples and try and get it to work. -There are a lot of issues that can happen but if lucky it'll work without too much problem. It should also -provide some output to debug with. + The best way to start is to grab the KerberosHttpClient.java file in examples and + try and get it to work. There are a lot of issues that can happen but if lucky it'll + work without too much problem. It should also provide some output to debug + with. - In windows it should default to using the logged in credentials, this -can be overridden by using 'kinit' e.g. $JAVA_HOME\bin\kinit testuser@AD.EXAMPLE.NET, which is very -helpful for testing and debugging issues. Remove the cache file created to revert back to the windows -Kerberos cache. - Make sure to list domain_realms in the krb5.conf file. This is a major source of problems. + In windows it should default to using the logged in credentials, this can be + overridden by using 'kinit' e.g. $JAVA_HOME\bin\kinit + testuser@AD.EXAMPLE.NET, which is very helpful for testing and + debugging issues. Remove the cache file created to revert back to the windows + Kerberos cache. + Make sure to list domain_realms in the krb5.conf file. This is a major source of + problems.
NegotiateSchemeFactory Class @@ -379,35 +428,44 @@ Kerberos cache.
setStripPort(boolean) - Strips the port off service names e.g. HTTP/webserver.ad.example.net:8080 -> HTTP/webserver.ad.example.net + Strips the port off service names e.g. HTTP/webserver.ad.example.net:8080 -> + HTTP/webserver.ad.example.net Found it useful when using JbossNegotiation.
setSpnegoCreate(boolean) - If using Java 1.5 or a Kerberos ticket an attempt will be made to wrap it up into a SPNEGO token. Again for JbossNegotiation. II7 accepts plain Kerberos tickets. + If using Java 1.5 or a Kerberos ticket an attempt will be made to wrap it up into + a SPNEGO token. Again for JbossNegotiation. II7 accepts plain Kerberos + tickets.
setSpengoGenerator(SpnegoTokenGenerator) - Inject a custom SpnegoTokenGenerator class to do the Kerberos to SPNEGO token wrapping. BouncySpnegoTokenGenerator example -is provided. This requires the BouncyCastle libs "http://www.bouncycastle.org/java.html" - + Inject a custom SpnegoTokenGenerator class to do the Kerberos to SPNEGO token + wrapping. BouncySpnegoTokenGenerator example is provided. This requires the + BouncyCastle libs "http://www.bouncycastle.org/java.html" +
GSS/Java Kerberos Setup. - This documentation assumes you are using windows but much of the informationapplies to Unix as well. - The org.ietf.jgss classes have lots of possible configuration parameters, -mainly in the krb5.conf/krb5.ini file. Some more info on the format at -http://web.mit.edu/kerberos/krb5-1.4/krb5-1.4.1/doc/krb5-admin/krb5.conf.html. + This documentation assumes you are using windows but much of the + informationapplies to Unix as well. + The org.ietf.jgss classes have lots of possible configuration parameters, mainly + in the krb5.conf/krb5.ini file. Some more info on the format at http://web.mit.edu/kerberos/krb5-1.4/krb5-1.4.1/doc/krb5-admin/krb5.conf.html.
login.conf file - The following configuration is a basic setup that works in Windows XP against both IIS7 and JbossNegotiate modules. - The system property that can be use to point to the login.conf file is java.security.auth.login.config. + The following configuration is a basic setup that works in Windows XP against both + IIS7 and JbossNegotiate modules. + The system property that can be use to point to the login.conf file is + java.security.auth.login.config. Sample usage... System.setProperty("java.security.auth.login.config", "login.conf"); @@ -432,7 +490,7 @@ com.sun.security.jgss.accept { If unspecified the system default will be used. Override with... System.setProperty("java.security.krb5.conf", "krb5.conf"); Example file contents... - +