httpcomponents-client/src/docbkx/authentication.xml

571 lines
31 KiB
XML
Raw Normal View History

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<!--
====================================================================
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.
====================================================================
-->
<chapter id="authentication">
<title>HTTP authentication</title>
<para>HttpClient provides full support for authentication schemes defined by the HTTP standard
specification. HttpClient's authentication framework can also be extended to support
non-standard authentication schemes such as <literal>NTLM</literal> and
<literal>SPNEGO</literal>.</para>
<section>
<title>User credentials</title>
<para>Any process of user authentication requires a set of credentials that can be used to
establish user identity. In the simplest form user crednetials can be just a user name /
password pair. <classname>UsernamePasswordCredentials</classname> represents a set of
credentials consisting of a security principal and a password in clear text. This
implementation is sufficient for standard authentication schemes defined by the HTTP
standard specification.</para>
<programlisting><![CDATA[
UsernamePasswordCredentials creds = new UsernamePasswordCredentials("user", "pwd");
System.out.println(creds.getUserPrincipal().getName());
System.out.println(creds.getPassword());
]]></programlisting>
<para>stdout &gt;</para>
<programlisting><![CDATA[
user
pwd
]]></programlisting>
<para><classname>NTCredentials</classname> is a Microsoft Windows specific implementation
that includes in addition to the user name / password pair a set of additional Windows
specific attributes such as a name of the user domain, as in Microsoft Windows network
the same user can belong to multiple domains with a different set of
authorizations.</para>
<programlisting><![CDATA[
NTCredentials creds = new NTCredentials("user", "pwd", "workstation", "domain");
System.out.println(creds.getUserPrincipal().getName());
System.out.println(creds.getPassword());
]]></programlisting>
<para>stdout &gt;</para>
<programlisting><![CDATA[
DOMAIN/user
pwd
]]></programlisting>
</section>
<section>
<title>Authentication schemes</title>
<para>The <interfacename>AuthScheme</interfacename> interface represents an abstract
challenge-response oriented authentication scheme. An authentication scheme is expected
to support the following functions:</para>
<itemizedlist>
<listitem>
<para>Parse and process the challenge sent by the target server in response to
request for a protected resource.</para>
</listitem>
<listitem>
<para>Provide properties of the processed challenge: the authentication scheme type
and its parameters, such the realm this authentication scheme is applicable to,
if available</para>
</listitem>
<listitem>
<para>Generate authorization string for the given set of credentials and the HTTP
request in response to the actual authorization challenge.</para>
</listitem>
</itemizedlist>
<para>Please note authentication schemes may be stateful involving a series of
challenge-response exchanges.</para>
<para>HttpClient ships with several <interfacename>AuthScheme</interfacename>
implementations:</para>
<itemizedlist>
<listitem>
<formalpara>
<title>Basic:</title>
<para>Basic authentication scheme as defined in RFC 2617. This authentication
scheme is insecure, as the credentials are transmitted in clear text.
Despite its insecurity Basic authentication scheme is perfectly adequate if
used in combination with the TLS/SSL encryption.</para>
</formalpara>
</listitem>
<listitem>
<formalpara>
<title>Digest</title>
<para>Digest authentication scheme as defined in RFC 2617. Digest authentication
scheme is significantly more secure than Basic and can be a good choice for
those applications that do not want the overhead of full transport security
through TLS/SSL encryption.</para>
</formalpara>
</listitem>
<listitem>
<formalpara>
<title>NTLM:</title>
<para>NTLM is a proprietary authentication scheme developed by Microsoft and
optimized for Windows platforms. NTLM is believed to be more secure than
Digest. This scheme is supported only partially and requires an external
NTLM engine. For details please refer to the
<literal>NTLM_SUPPORT.txt</literal> document included with HttpClient
distributions.</para>
</formalpara>
</listitem>
<listitem>
<formalpara>
<title>SPNEGO/Kerberos:</title>
<para><literal>SPNEGO</literal> (<emphasis>S</emphasis>imple and
<emphasis>P</emphasis>rotected <literal>GSSAPI</literal>
<emphasis>Nego</emphasis>tiation Mechanism) is a <literal>GSSAPI</literal>
"pseudo mechanism" that is used to negotiate one of a number of possible
real mechanisms. SPNEGO's most visible use is in Microsoft's <literal>HTTP
Negotiate</literal> authentication extension. The negotiable
sub-mechanisms include NTLM and Kerberos supported by Active Directory.
Presently HttpClient supports Kerberos sub-mechanism only. </para>
</formalpara>
</listitem>
</itemizedlist>
</section>
<section>
<title>HTTP authentication parameters</title>
<para>These are parameters that be used to customize HTTP authentication process and
behaviour of individual authentication schemes:</para>
<itemizedlist>
<listitem>
<formalpara>
<title>'http.protocol.handle-authentication':</title>
<para>defines whether authentication should be handled automatically. This
parameter expects a value of type <classname>java.lang.Boolean</classname>.
If this parameter is not set HttpClient will handle authentication
automatically.</para>
</formalpara>
</listitem>
<listitem>
<formalpara>
<title>'http.auth.credential-charset':</title>
<para>defines the charset to be used when encoding user credentials. This
parameter expects a value of type <literal>java.lang.String</literal>. If
this parameter is not set <literal>US-ASCII</literal> will be used.</para>
</formalpara>
</listitem>
<listitem>
<formalpara>
<title>'http.auth.target-scheme-pref':</title>
<para>Defines the order of preference for supported
<interfacename>AuthScheme</interfacename>s when authenticating with the
target host. This parameter expects a value of type
<interface>java.util.Collection</interface>. The collection is expected
to contain <classname>java.lang.String</classname> instances representing
an id of an authentication scheme.</para>
</formalpara>
</listitem>
<listitem>
<formalpara>
<title>'http.auth.proxy-scheme-pref':</title>
<para>Defines the order of preference for supported
<interfacename>AuthScheme</interfacename>s when authenticating with the
proxy host. This parameter expects a value of type
<interface>java.util.Collection</interface>. The collection is expected
to contain <classname>java.lang.String</classname> instances representing
an id of an authentication scheme.</para>
</formalpara>
</listitem>
</itemizedlist>
<para>For example, one can force HttpClient to use a different order of preference for
authentication schemes</para>
<programlisting><![CDATA[
DefaultHttpClient httpclient = new DefaultHttpClient(ccm, params);
// Choose BASIC over DIGEST for proxy authentication
List<String> authpref = new ArrayList<String>();
authpref.add(AuthPolicy.BASIC);
authpref.add(AuthPolicy.DIGEST);
httpclient.getParams().setParameter(AuthPNames.PROXY_AUTH_PREF, authpref);
]]></programlisting>
</section>
<section>
<title>Authentication scheme registry</title>
<para>HttpClient maintains a registry of available authentication scheme using
<classname>AuthSchemeRegistry</classname> class. The following schemes are
registered per default:</para>
<itemizedlist>
<listitem>
<formalpara>
<title>Basic:</title>
<para>Basic authentication scheme</para>
</formalpara>
</listitem>
<listitem>
<formalpara>
<title>Digest:</title>
<para>Digest authentication scheme</para>
</formalpara>
</listitem>
</itemizedlist>
<para>Please note <literal>NTLM</literal> and <literal>SPNEGO</literal> schemes are
<emphasis>NOT</emphasis> registered per default. For details on how to enable
<literal>NTLM</literal> support please refer to the
<literal>NTLM_SUPPORT.txt</literal> document included with HttpClient distributions.
<literal>SPNEGO</literal> setup tends to be system specific and must be properly
configured in order to be functional. See section on <link linkend="spnego">SPNEGO
authentication </link> for details.</para>
</section>
<section>
<title>Credentials provider</title>
<para>Credentials providers are intended to maintain a set of user credentials and to be
able to produce user credentials for a particular authentication scope. Authentication
scope consists of a host name, a port number, a realm name and an authentication scheme
name. When registering credentials with the credentials provider one can provide a wild
card (any host, any port, any realm, any scheme) instead of a concrete attribute value.
The credentials provider is then expected to be able to find the closest match for a
particular scope if the direct match cannot be found.</para>
<para>HttpClient can work with any physical representation of a credentials provider that
implements the <interfacename>CredentialsProvider</interfacename> interface. The default
<interfacename>CredentialsProvider</interfacename> implementation called
<classname>BasicCredentialsProvider</classname> is a simple implementation backed by
a <classname>java.util.HashMap</classname>.</para>
<programlisting><![CDATA[
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("somehost", AuthScope.ANY_PORT),
new UsernamePasswordCredentials("u1", "p1"));
credsProvider.setCredentials(
new AuthScope("somehost", 8080),
new UsernamePasswordCredentials("u2", "p2"));
credsProvider.setCredentials(
new AuthScope("otherhost", 8080, AuthScope.ANY_REALM, "ntlm"),
new UsernamePasswordCredentials("u3", "p3"));
System.out.println(credsProvider.getCredentials(
new AuthScope("somehost", 80, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("somehost", 8080, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("otherhost", 8080, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("otherhost", 8080, null, "ntlm")));
]]></programlisting>
<para>stdout &gt;</para>
<programlisting><![CDATA[
[principal: u1]
[principal: u2]
null
[principal: u3]
]]></programlisting>
</section>
<section>
<title>HTTP authentication and execution context</title>
<para>HttpClient relies on the <classname>AuthState</classname> class to keep track of
detailed information about the state of the authentication process. HttpClient creates
two instances of <classname>AuthState</classname> in the course of HTTP request
execution: one for target host authentication and another one for proxy authentication.
In case the target server or the proxy require user authentication the respective
<classname>AuthScope</classname> instance will be populated with the
<classname>AuthScope</classname>, <interfacename>AuthScheme</interfacename> and
<interfacename>Crednetials</interfacename> used during the authentication process.
The <classname>AuthState</classname> can be examined in order to find out what kind of
authentication was requested, whether a matching
<interfacename>AuthScheme</interfacename> implementation was found and whether the
credentials provider managed to find user credentials for the given authentication
scope.</para>
<para>In the course of HTTP request execution HttpClient adds the following authentication
related objects to the execution context:</para>
<itemizedlist>
<listitem>
<formalpara>
<title>'http.authscheme-registry':</title>
<para><classname>AuthSchemeRegistry</classname> instance representing the actual
authentication scheme registry. The value of this attribute set in the local
context takes precedence over the default one.</para>
</formalpara>
</listitem>
<listitem>
<formalpara>
<title>'http.auth.credentials-provider':</title>
<para><interfacename>CookieSpec</interfacename> instance representing the actual
credentials provider. The value of this attribute set in the local context
takes precedence over the default one.</para>
</formalpara>
</listitem>
<listitem>
<formalpara>
<title>'http.auth.target-scope':</title>
<para><classname>AuthState</classname> instance representing the actual target
authentication state. The value of this attribute set in the local context
takes precedence over the default one.</para>
</formalpara>
</listitem>
<listitem>
<formalpara>
<title>'http.auth.proxy-scope':</title>
<para><classname>AuthState</classname> instance representing the actual proxy
authentication state. 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
the request has been executed:</para>
<programlisting><![CDATA[
HttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("http://localhost:8080/");
HttpResponse response = httpclient.execute(httpget, localContext);
AuthState proxyAuthState = (AuthState) localContext.getAttribute(
ClientContext.PROXY_AUTH_STATE);
System.out.println("Proxy auth scope: " + proxyAuthState.getAuthScope());
System.out.println("Proxy auth scheme: " + proxyAuthState.getAuthScheme());
System.out.println("Proxy auth credentials: " + proxyAuthState.getCredentials());
AuthState targetAuthState = (AuthState) localContext.getAttribute(
ClientContext.TARGET_AUTH_STATE);
System.out.println("Target auth scope: " + targetAuthState.getAuthScope());
System.out.println("Target auth scheme: " + targetAuthState.getAuthScheme());
System.out.println("Target auth credentials: " + targetAuthState.getCredentials());
]]></programlisting>
</section>
<section>
<title>Preemptive authentication</title>
<para>HttpClient does not support preemptive authentication out of the box, because if
misused or used incorrectly the preemptive authentication can lead to significant
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>
<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);
}
}
}
};
DefaultHttpClient httpclient = new DefaultHttpClient();
// Add as the very first interceptor in the protocol chain
httpclient.addRequestInterceptor(preemptiveAuth, 0);
]]></programlisting>
</section>
<section id="spnego">
<title><literal>SPNEGO</literal>/Kerberos Authentication</title>
<para><literal>SPNEGO</literal> (<emphasis>S</emphasis>imple and
<emphasis>P</emphasis>rotected <literal>GSSAPI</literal>
<emphasis>Nego</emphasis>tiation 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. </para>
<sidebar>
<orderedlist numeration="arabic">
<listitem>
<para>Client Web Browser does HTTP GET for resource.</para>
</listitem>
<listitem>
<para>Web server returns HTTP 401 status and a header:
<literal>WWW-Authenticate: Negotiate</literal></para>
</listitem>
<listitem>
<para>Client generates a <literal>NegTokenInit</literal>, base64 encodes it, and
resubmits the <literal>GET</literal> with an Authorization header:
<literal>Authorization: Negotiate &lt;base64
encoding&gt;</literal>.</para>
</listitem>
<listitem>
<para>Server decodes the <literal>NegTokenInit</literal>, extracts the supported
<literal>MechTypes</literal> (only Kerberos V5 in our case), ensures it
is one of the expected ones, and then extracts the
<literal>MechToken</literal> (Kerberos Token) and authenticates
it.</para>
<para>If more processing is required another HTTP 401 is returned to the client
with more data in the the <literal>WWW-Authenticate</literal> header. Client
takes the info and generates another token passing this back in the
<literal>Authorization</literal> header until complete.</para>
</listitem>
<listitem>
<para>When the client has been authenticated the Web server should return the
HTTP 200 status, a final <literal>WWW-Authenticate</literal> header and the
page content.</para>
</listitem>
</orderedlist>
</sidebar>
<section>
<title><literal>SPNEGO</literal> support in HttpClient</title>
<para><literal>SPNEGO</literal> authentication scheme is compatible with Sun Java
versions 1.5 and up.</para>
<para>The Sun JRE provides the supporting classes to do nearly all the kerberos and
<literal>SPNEGO</literal> token handling. This means that a lot of the setup is
for the GSS classes. The <classname>NegotiateScheme</classname> is a simple class to
handle marshalling the tokens and reading and writing the correct headers.</para>
<para>The best way to start is to grab the <literal>KerberosHttpClient.java</literal>
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.</para>
<para>In windows it should default to using the logged in credentials, this can be
overridden by using 'kinit' e.g. <literal>$JAVA_HOME\bin\kinit
testuser@AD.EXAMPLE.NET</literal>, which is very helpful for testing and
debugging issues. Remove the cache file created to revert back to the windows
Kerberos cache.</para>
<para>Make sure to list <literal>domain_realms</literal> in the
<literal>krb5.conf</literal> file. This is a major source of problems.</para>
</section>
<section>
<title>GSS/Java Kerberos Setup</title>
<para>This documentation assumes you are using windows but much of the information
applies to Unix as well.</para>
<para>The <classname>org.ietf.jgss</classname> classes have lots of possible
configuration parameters, mainly in the
<literal>krb5.conf</literal>/<literal>krb5.ini</literal> file. Some more info on
the format at <ulink
url="http://web.mit.edu/kerberos/krb5-1.4/krb5-1.4.1/doc/krb5-admin/krb5.conf.html"
>http://web.mit.edu/kerberos/krb5-1.4/krb5-1.4.1/doc/krb5-admin/krb5.conf.html</ulink>.</para>
</section>
<section>
<title><literal>login.conf</literal> file</title>
<para>The following configuration is a basic setup that works in Windows XP against both
<literal>IIS7</literal> and <literal>JbossNegotiate</literal> modules.</para>
<para>The system property <literal>java.security.auth.login.config</literal> can be use
to point at the <literal>login.conf</literal> file.</para>
<para><literal>login.conf</literal> content may look like the following:</para>
<programlisting><![CDATA[
com.sun.security.jgss.login {
com.sun.security.auth.module.Krb5LoginModule required client=TRUE useTicketCache=true;
};
com.sun.security.jgss.initiate {
com.sun.security.auth.module.Krb5LoginModule required client=TRUE useTicketCache=true;
};
com.sun.security.jgss.accept {
com.sun.security.auth.module.Krb5LoginModule required client=TRUE useTicketCache=true;
};
]]>
</programlisting>
</section>
<section>
<title><literal>krb5.conf</literal> / <literal>krb5.ini</literal> file</title>
<para>If unspecified the system default will be used. Override if needed by the setting
system property <literal>java.security.krb5.conf</literal> to point at a custom
<literal>krb5.conf</literal> file.</para>
<para><literal>krb5.conf</literal> content may look like the following:</para>
<programlisting><![CDATA[
[libdefaults]
default_realm = AD.EXAMPLE.NET
udp_preference_limit = 1
[realms]
AD.EXAMPLE.NET = {
kdc = KDC.AD.EXAMPLE.NET
}
[domain_realms]
.ad.example.net=AD.EXAMPLE.NET
ad.example.net=AD.EXAMPLE.NET
]]>
</programlisting>
</section>
<section>
<title>Windows Specific configuration</title>
<para>To allow windows to use the current users tickets system property
<literal>javax.security.auth.useSubjectCredsOnly</literal> must be set to
<literal>false</literal> and Windows registry key
<literal>allowtgtsessionkey</literal> should be added and set correctly to allow
session keys to be sent in the Kerberos Ticket-Granting Ticket.</para>
<para>On the Windows Server 2003 and Windows 2000 SP4, here is the required registry
setting:</para>
<programlisting><![CDATA[
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters
Value Name: allowtgtsessionkey
Value Type: REG_DWORD
Value: 0x01
]]>
</programlisting>
<para>Here is the location of the registry setting on Windows XP SP2:</para>
<programlisting><![CDATA[
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos\
Value Name: allowtgtsessionkey
Value Type: REG_DWORD
Value: 0x01
]]>
</programlisting>
</section>
<section>
<title>Activating and customizing <literal>SPNEGO</literal> authentication
scheme</title>
<para>Please note <literal>SPNEGO</literal> authentication scheme is NOT active per
default! </para>
<para>In order to activate <literal>SPNEGO</literal> support an instance of
<classname>NegotiateSchemeFactory</classname> class must be created and
registered with the authentication scheme registry of HttpClient. </para>
<programlisting><![CDATA[
DefaultHttpClient httpclient = new DefaultHttpClient();
NegotiateSchemeFactory nsf = new NegotiateSchemeFactory();
httpclient.getAuthSchemes().register(AuthPolicy.SPNEGO, nsf);
]]>
</programlisting>
<para>There are several methods that can be used to customize the behaviour of
<classname>NegotiateSchemeFactory</classname>. </para>
<section>
<title>
<methodname>setStripPort</methodname>
</title>
<para>Strips the port off service names e.g.
<literal>HTTP/webserver.ad.example.net:8080</literal> ->
<literal>HTTP/webserver.ad.example.net</literal></para>
<para>Found it useful when using JbossNegotiation.</para>
</section>
<section>
<title>
<methodname>setSpnegoCreate</methodname>
</title>
<para>If using Java 1.5 or a Kerberos ticket an attempt will be made to wrap it up
into a <literal>SPNEGO</literal> token. Again for JbossNegotiation. II7 accepts
plain Kerberos tickets.</para>
</section>
<section>
<title>
<methodname>setSpengoGenerator</methodname>
</title>
<para>Use this method to inject a custom
<interfacename>SpnegoTokenGenerator</interfacename> class to do the Kerberos
to <literal>SPNEGO</literal> token wrapping.
<classname>BouncySpnegoTokenGenerator</classname> implementation is provided
as unsupported contribution from the contrib package. This requires the
BouncyCastle libs <ulink url="http://www.bouncycastle.org/java.html"
>"http://www.bouncycastle.org/java.html"</ulink>
</para>
</section>
</section>
</section>
</chapter>