HttpClient为Kerberos身份验证设置凭据

16

我正在尝试通过Kerberos/HTTP主机进行身份验证。使用Apache HttpClient作为我的客户端,并使用这个源代码的稍微修改版。我的Kerberos身份验证进展顺利,我希望知道如何在程序中设置登录凭据。目前,凭据是通过控制台手动输入的,但我希望在运行时由我选择。[实际上,我希望自动化并负载测试服务器,使用大量用户。]

编辑:以下是相关部分的代码片段:

..
        NegotiateSchemeFactory nsf = new NegotiateSchemeFactory();        
        httpclient.getAuthSchemes().register(AuthPolicy.SPNEGO, nsf);

        Credentials use_jaas_creds = new Credentials() {

            public String getPassword() {
                return null;
            }

            public Principal getUserPrincipal() {
                return null;
            }    
        };

        httpclient.getCredentialsProvider().setCredentials(
                new AuthScope(null, -1, null),
                use_jaas_creds);

        HttpUriRequest request = new HttpGet("http://kerberoshost/");
        HttpResponse response = httpclient.execute(request);
 .. 

接口 Credentials 有两个方法 - getPassword()getUserPrincipal(),但是根据我所做的一些调试,它们似乎根本没有被调用。

我错过了什么?有什么更清洁的方法来静态设置凭证吗?

之前曾经有类似的问题,但针对keytabs / login.conf的hack太麻烦,对于大量用户凭证的自动化负载测试来说也不是实际选项。感谢任何帮助。


在C语言中,您可以使用krb5_get_init_creds_password例程。在我查找的几分钟内,我没有在Java Kerberos API中找到直接对应的类比。 http://web.mit.edu/kerberos/krb5-devel/doc/appdev/refs/api/krb5_get_init_creds_password.html - Fred the Magic Wonder Dog
@FredtheMagicWonderDog,对我来说C并不是一个真正的选择。因为测试用例不仅涉及到Kerberos,还涉及到HTTP。测试是基于HTTP成功重定向状态码等进行验证的。但如果有任何现有的实用程序(C/命令行)可以同时处理Kerberos和HTTP,我很乐意尝试。我不知道是否存在这样的工具。 - Alavalathi
我相当确定curl可以做到这一点。curl -V curl 7.19.7(x86_64-redhat-linux-gnu)libcurl / 7.19.7 NSS / 3.14.3.0 zlib / 1.2.3 libidn / 1.18 libssh2 / 1.4.2 协议:tftp ftp telnet dict ldap ldaps http file https ftps scp sftp 功能:GSS-Negotiate IDN IPv6 Largefile NTLM SSL libz - Fred the Magic Wonder Dog
我相信有Java版本的kinit,所以API必须存在于某个地方。但我不是很擅长Java。也许这个问题可以帮到你:https://dev59.com/PkjSa4cB1Zd3GeqPCyNo - Fred the Magic Wonder Dog
@FredtheMagicWonderDog 嗯,好的。我会尝试使用curl。虽然这不是直接回答问题,但它可能会帮助我解决实际问题!:) 感谢你的指引。 - Alavalathi
1个回答

24
由于SPNEGO的存在,您发布的代码片段(凭据类设置)并未被httpclient用于身份验证。
您可以使用DoAs + CallBackhandler在运行时传递用户名和密码。
然后,您需要一个login.conf或类似名称的文件,并将其包含在其中:
KrbLogin{
 com.sun.security.auth.module.Krb5LoginModule required doNotPrompt=false debug=true useTicketCache=false;
};
您可以将“KrbLogin”的名称更改为您喜欢的名称(请记得在Java代码中使用相同的名称),并使用Java系统属性进行设置。
System.setProperty("java.security.auth.login.config", "login.conf");

或者使用

-Djava.security.auth.login.config=login.config

接下来您需要一个krb5配置文件(通常为krb5.ini或krb5.conf,其中包含正确的配置)

如果您的工作站(或服务器)已正确配置Kerberos,则此类应该按原样工作(使用正确的登录文件login.conf和krb5.ini)。我使用httpclient 4.3.3和java 1.7进行测试:

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.impl.auth.SPNegoSchemeFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import javax.security.auth.Subject;
import javax.security.auth.callback.*;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.io.IOException;
import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.util.Set;

public class HttpClientKerberosDoAS {

    public static void main(String[] args) throws Exception {

        System.setProperty("java.security.auth.login.config", "login.conf");
        System.setProperty("java.security.krb5.conf", "krb5.conf");
        System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");

        String user = "";
        String password = "";
        String url = "";

        if (args.length == 3) {
            user = args[0];
            password = args[1];
            url = args[2];


            HttpClientKerberosDoAS kcd = new HttpClientKerberosDoAS();

            System.out.println("Loggin in with user [" + user + "] password [" + password + "] ");
            kcd.test(user, password, url);
        } else {
            System.out.println("run with User Password URL");
        }

    }

    public void test(String user, String password, final String url) {
        try {

            LoginContext loginCOntext = new LoginContext("KrbLogin", new KerberosCallBackHandler(user, password));
            loginCOntext.login();

            PrivilegedAction sendAction = new PrivilegedAction() {

                @Override
                public Object run() {
                    try {

                        Subject current = Subject.getSubject(AccessController.getContext());
                        System.out.println("----------------------------------------");
                        Set<Principal> principals = current.getPrincipals();
                        for (Principal next : principals) {
                            System.out.println("DOAS Principal: " + next.getName());
                        }
                        System.out.println("----------------------------------------");

                        call(url);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    return true;
                }
            };

            Subject.doAs(loginCOntext.getSubject(), sendAction);

        } catch (LoginException le) {
            le.printStackTrace();
        }
    }

    private void call(String url) throws IOException {
        HttpClient httpclient = getHttpClient();

        try {

            HttpUriRequest request = new HttpGet(url);
            HttpResponse response = httpclient.execute(request);
            HttpEntity entity = response.getEntity();

            System.out.println("----------------------------------------");

            System.out.println("STATUS >> " + response.getStatusLine());

            if (entity != null) {
                System.out.println("RESULT >> " + EntityUtils.toString(entity));
            }

            System.out.println("----------------------------------------");

            EntityUtils.consume(entity);

        } finally {
            httpclient.getConnectionManager().shutdown();
        }
    }

    private  HttpClient getHttpClient() {

        Credentials use_jaas_creds = new Credentials() {
            public String getPassword() {
                return null;
            }

            public Principal getUserPrincipal() {
                return null;
            }
        };

        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(new AuthScope(null, -1, null), use_jaas_creds);
        Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create().register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(true)).build();
        CloseableHttpClient httpclient = HttpClients.custom().setDefaultAuthSchemeRegistry(authSchemeRegistry).setDefaultCredentialsProvider(credsProvider).build();

        return httpclient;
    }

    class KerberosCallBackHandler implements CallbackHandler {

        private final String user;
        private final String password;

        public KerberosCallBackHandler(String user, String password) {
            this.user = user;
            this.password = password;
        }

        public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

            for (Callback callback : callbacks) {

                if (callback instanceof NameCallback) {
                    NameCallback nc = (NameCallback) callback;
                    nc.setName(user);
                } else if (callback instanceof PasswordCallback) {
                    PasswordCallback pc = (PasswordCallback) callback;
                    pc.setPassword(password.toCharArray());
                } else {
                    throw new UnsupportedCallbackException(callback, "Unknown Callback");
                }

            }
        }
    }

}

注意:

您可以使用:

System.setProperty("sun.security.krb5.debug", "true");
或:
-Dsun.security.krb5.debug=true

调查问题。


谢谢。我从未有机会尝试那个。与此同时,我已经找到了另一种使用curl等工具的方法。 - Alavalathi
1
完美运行 - 谢谢!对于使用纯Java URLConnection的人来说,设置默认验证器可能更容易 - 请参见https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/lab/part6.html#Proxy_Authentication - Juraj Martinka
1
如果您使用的是Windows,您可能需要配置注册表项。这篇文章帮助我解决了问题http://cr.openjdk.java.net/~weijun/special/krb5winguide-2/raw_files/new/kwin ,同时也稍微记录了一下login.conf。您还可以使用KerberosCredentials,而不是像上面原始问题中实现'Credentials',这样会更加整洁。 - Martin Cassidy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接