| Documentation Contents

## Part VI: HTTP/SPNEGO Authentication

- Exercise 9: Using HTTP/SPNEGO Authentication
- What is HTTP SPNEGO
  - Web Authentication
  - Proxy Authentication
- How to use HTTP/SPNEGO Authentication
  - Kerberos 5 Configuration
  - Username and Password Retrieval
  - Scheme Preference
  - Fallback
- Example

## Exercise 9: Using HTTP/SPNEGO Authentication

## What is HTTP SPNEGO

HTTP SPNEGO supports the Negotiate authentication scheme in an HTTP communication. SPNEGO supports types of authentication:

### Web 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

### Proxy Authentication

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.

## How to use HTTP/SPNEGO Authentication

There is no new public API function involved in the new feature, but several configurations are needed to perform a success communication:

### Kerberos 5 Configuration

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:

- Some way to provide Kerberos configurations. This can be achieved with the Java system property `java.security.krb5.conf`. For example:

                java -Djava.security.krb5.conf=krb5.conf \
                     -Djavax.security.auth.useSubjectCredsOnly=false \
                     ClassName

- A JAAS config file denoting what login module to use. HTTP SPNEGO codes will look for the standard entry named `com.sun.security.jgss.krb5.initiate`.

For example, you can provide a file `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

### Username and Password Retrieval

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".

This means your Authenticator implementation will look like:

        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.

### Scheme Preference

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:

- GSS/SPNEGO -> Digest -> NTLM -> Basic

Noticed that Kerberos does not appear in this list, since whenever Negotiate is supported, GSS/SPNEGO is always chosen.

### Fallback

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 : If `http.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.

## Example

Assume that you have an IIS Server running on a Windows Server within an Active Directory. A web page on this server is configured to be protected by Integrated Windows Authentication. This means the server will prompt for both Negotiate and NTLM authentication.

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>

Copyright © 1995-2010 Sun Microsystems, Inc. All Rights Reserved. Please send comments using this Feedback page. | Java Technology