How do I authenticate with Spnego/Kerberos and Apache's HttpClient?
Asked Answered
G

1

15

How do I correctly setup a connection with HttpClient that uses the logged in user's ActiveDirectory credentials to authenticate against a website and requires Kerberos/Spnego authentication?

Genethlialogy answered 8/7, 2014 at 13:40 Comment(0)
G
19

Here's an example:

public class HttpSpnegoConnection {
        
    /**
     * Uses HttpClient 4.3.4 and CommonsIO 2.4
     */
    public static void main(String[] args) throws ClientProtocolException, IOException {
        
        
        String domain = "yourdomain.com";
        String kdc =  "thekdc.yourdomain.ch";
        // careful with aliases, 'somewebsite' has to be in the kerberos database
        String url = "http://somewebsite:8080/myresource/";
        // if the port does not match the kerberos database entry, skip it during the lookup
        boolean skipPortAtKerberosDatabaseLookup = true;

        // Depending on your AD configuration, this file is not needed
        File krb5Config = createKrb5Configuration(domain,kdc);
        
        System.out.println("Created config '"+krb5Config.getAbsolutePath()+"':");
        System.out.println(IOUtils.toString(new FileReader(krb5Config)));
        
        System.setProperty("java.security.krb5.conf", krb5Config.toURI().toString());
        System.setProperty("sun.security.krb5.debug", "true");
        System.setProperty("javax.security.auth.useSubjectCredsOnly","false");
        
        
        Lookup<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
            .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(skipPortAtKerberosDatabaseLookup))
            .build();
        
        CloseableHttpClient client = HttpClients.custom().setDefaultAuthSchemeRegistry(authSchemeRegistry).build();
        HttpClientContext context = HttpClientContext.create();
        BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();

        // This may seem odd, but specifying 'null' as principal tells java to use the logged in user's credentials
        Credentials useJaasCreds = new Credentials() {

            public String getPassword() {
                return null;
            }

            public Principal getUserPrincipal() {
                return null;
            }

        };
        credentialsProvider.setCredentials( new AuthScope(null, -1, null), useJaasCreds );
        context.setCredentialsProvider(credentialsProvider);
        
        HttpGet httpget = new HttpGet(url);
        CloseableHttpResponse response = client.execute(httpget,context);
        String responseString = IOUtils.toString(response.getEntity().getContent());
        
        System.out.println("HTTP Response:");
        System.out.println(responseString);
    }
    
    /** 
        Creates a temporary krb5.conf
        [libdefaults]
            default_realm = <domain>
            
        [realms]
            snb.ch = {
                kdc = <kdc>
                admin_server = <kdc>
            }
     */
    private static File createKrb5Configuration(String domain, String kdc) throws IOException {
        File tempFile = File.createTempFile("krb5", "kdc");
        ArrayList<String> lines = new ArrayList<>();
        lines.add("[libdefaults]");
        lines.add("\tdefault_realm = "+domain);
        lines.add("[realms]");
        lines.add("\t"+domain+" = {");
        lines.add("\t\tkdc = "+kdc);
        lines.add("\t\tadmin_server = "+kdc);
        lines.add("\t}");
        FileWriter writer = new FileWriter(tempFile);
        IOUtils.writeLines(lines, System.lineSeparator(), writer);
        IOUtils.closeQuietly(writer);
        return tempFile;
    }
    
}

Depending on your AD configuration, creating and specifying the krb5.conf may be skipped.

Genethlialogy answered 8/7, 2014 at 13:40 Comment(3)
When I run this (after customising domain/kdc/url), it prompts me for a password (rather than using my logged-in credentials).Overbuild
@Overbuild I cannot tell you what the problem is, but if you get a prompt this usually means that kerberos authetication failed or is not set up properly.Genethlialogy
It works in my case. You have to put the code inside doAs e.g Subject.doAs(subject, new PrivilegedExceptionAction<Void>()Cahan

© 2022 - 2024 — McMap. All rights reserved.