HTTP SPNEGO supports the Negotiate authentication scheme in an HTTP communication. SPNEGO supports types of authentication:
The Web Server responds with
HTTP/1.1 401 Unauthorized WWW-Authenticate: Negotiate
the client will need to send a header like
Authorization: Negotiate YY.....
to authenticate itself to the server
The Web Server responses with
HTTP/1.1 407 Proxy Authentication Required Proxy-Authenticate: Negotiate
the client will need to send a header like
Proxy-Authorization: Negotiate YY.....
to authenticate itself to the proxy server
This feature supports both types of authentication.
There is no new public API function involved in the new feature, but several configurations are needed to perform a success communication:
Since the SPNEGO mechanism will call JGSS, which in turns calls the Kerberos V5 login module to do real works. Kerberos 5 configurations are needed. This includes the following:
java.security.krb5.conf
. For example:java -Djava.security.krb5.conf=krb5.conf \ -Djavax.security.auth.useSubjectCredsOnly=false \ ClassName
com.sun.security.jgss.krb5.initiate
.spnegoLogin.conf
:
com.sun.security.jgss.krb5.initiate { com.sun.security.auth.module.Krb5LoginModule required useTicketCache=true; };
and run java with:
java -Djava.security.krb5.conf=krb5.conf \ -Djava.security.auth.login.config=spnegoLogin.conf \ -Djavax.security.auth.useSubjectCredsOnly=false \ ClassName
Just like any other HTTP authentication scheme, the client can
provide a customized java.net.Authenticator
to feed
username and password to the HTTP SPNEGO module if
they are needed (i.e. there is no credential cache available). The
only authentication information needed to be checked in your
Authenticator is the scheme which can be retrieved with
getRequestingScheme()
. The value should be
"Negotiate".
class MyAuthenticator extends Authenticator { public PasswordAuthentication getPasswordAuthentication () { if (getRequestingScheme().equalsIgnoreCase("negotiate")) { String krb5user; char[] krb5pass; // get krb5user and krb5pass in your own way .... return (new PasswordAuthentication (krb5user, krb5pass)); } else { .... } } }
Note : According to the specification of
java.net.Authenticator
, it's designed to get the
username and password at the same time, so do not specify
principal=xxx
in the JAAS config file.
The client can still provide system property
http.auth.preference
to denote that a certain scheme
should always be used as long as the server request for it. You can
use "SPNEGO" or "Kerberos" for this system property. "SPNEGO" means
you prefer to response the Negotiate scheme using the GSS/SPNEGO
mechanism; "Kerberos" means you prefer to response the Negotiate
scheme using the GSS/Kerberos mechanism. Normally, when
authenticating against a Microsoft product, you can use "SPNEGO".
The value "Kerberos" also works for Microsoft servers. It's only
needed when you encounter a server which knows Negotiate but
doesn't know about SPNEGO.
If http.auth.preference
is not set, the internal
order choosen is:
Noticed that Kerberos does not appear in this list, since whenever Negotiate is supported, GSS/SPNEGO is always chosen.
If the server has provided more than one authentication scheme (including Negotiate), according to the processing order mentioned in the last section, Java will try to challenge the Negotiate scheme. However, if the protocol cannot be established successfully (for example; The kerberos configuration is not correct, or the server's hostname is not recorded in the KDC principal DB, or the username and password provided by Authenticator is wrong), then the second strongest scheme will be automatically used.
Note : Ifhttp.auth.preference
is set
to SPNEGO or Kerberos, then SPNEGO assumes you only want to try the
Negotiate scheme even if it fails. SPNEGO will not fallback to any
other scheme and your program will throw an
IOException
saying it received a 401 or 407 error from
the HTTP response.
You need to prepare these files to get the protected file:
Code listing for RunHttpSpnego.java
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.Authenticator; import java.net.PasswordAuthentication; import java.net.URL; public class RunHttpSpnego { static final String kuser = "username"; // your account name static final String kpass = "password"; // your password for the account static class MyAuthenticator extends Authenticator { public PasswordAuthentication getPasswordAuthentication() { // I haven't checked getRequestingScheme() here, since for NTLM // and Negotiate, the usrname and password are all the same. System.err.println("Feeding username and password for "
+ getRequestingScheme()); return (new PasswordAuthentication(kuser, kpass.toCharArray())); } } public static void main(String[] args) throws Exception { Authenticator.setDefault(new MyAuthenticator()); URL url = new URL(args[0]); InputStream ins = url.openConnection().getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(ins)); String str; while((str = reader.readLine()) != null) System.out.println(str); } }
Code listing for krb.conf
[libdefaults] default_realm = AD.LOCAL [realms] AD.LOCAL = { kdc = kdc.ad.local }
Code listing for login.conf
com.sun.security.jgss.krb5.initiate { com.sun.security.auth.module.Krb5LoginModule required
doNotPrompt=false useTicketCache=true; };
Then, compile RunHttpSpnego.java
and run
java -Djava.security.krb5.conf=krb5.conf \ -Djava.security.auth.login.config=login.conf \ -Djavax.security.auth.useSubjectCredsOnly=false \ RunHttpSpnego \ http://www.ad.local/hello/hello.html
You will see:
Feeding username and password for Negotiate <h1>Hello, You got me!</h1>
In fact, if you are running on a Windows machine as a domain
user, or, you are running on a Linux or Solaris machine that has
already issued the kinit
command and got the
credential cache. The class MyAuthenticator
will be
completely ignored, and the output will be simply
<h1>Hello, You got me!</h1>
which shows the username and password are not consulted. This is the so-called Single Sign-On.
Also, You can just run
java RunHttpSpnego \ http://www.ad.local/hello/hello.html
to see how the fallback is done, in which case you will see
Feeding username and password for ntlm <h1>Hello, You got me!</h1>