Java HttpClient 为 Kerberos 身份验证设置凭据

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21629132/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 09:45:09  来源:igfitidea点击:

HttpClient set credentials for Kerberos authentication

javaauthenticationhttpclientkerberosuserprincipal

提问by user30622

I am trying to authenticate with a kerberos/HTTP host. Using Apache HttpClient as my client - and a slightly modified version of this source.My Kerberos authentication goes perfectly fine, and I wish to know how to set the login credentials programatically. At the moment, the credentials are entered manually through the console, but I want to have it chosen by me at run time. [ As I wish to automate and load test the server with a large number of users, actually. ].

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

EDIT : Here is a code snippet of the relevant parts :

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

..
        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);
 .. 

The interface Credentialshas two methods - getPassword()and getUserPrincipal(), but from some debugging I did, they don't seem to be invoked at all.

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

What am I missing here ? What is a cleaner way to statically set the credentials ?

我在这里错过了什么?静态设置凭据的更简洁方法是什么?

A very similar question had been asked before, but keytabs/login.conf hack is too cumbersome and not a practical option for an automated load test with a large number of user credentials. Appreciate any help on this.

之前已经问过一个非常相似的问题,但是 keytabs/login.conf hack 太麻烦,对于具有大量用户凭据的自动负载测试来说不是一个实用的选项。感谢您对此的任何帮助。

采纳答案by eljeko

Because of SPNEGO the snippet code you post (Credentials class stuff setup) is not used by httpclient to authenticate.

由于 SPNEGO,httpclient 不使用您发布的片段代码(凭据类内容设置)进行身份验证。

You can use a DoAs + a CallBackhandler to pass user & password at runtime.

您可以使用 DoAs + CallBackhandler 在运行时传递用户和密码。

Then you need a login.conf or whatever the name with this inside:

然后你需要一个 login.conf 或者里面有这个名字的任何名字:

KrbLogin{
 com.sun.security.auth.module.Krb5LoginModule required doNotPrompt=false debug=true useTicketCache=false;
};

You can change the name from "KrbLogin" to the name you like (remember to use the same name in your java code)

您可以将名称从“KrbLogin”更改为您喜欢的名称(记住在您的java代码中使用相同的名称)

and set this with java system properties:

并使用 java 系统属性设置它:

System.setProperty("java.security.auth.login.config", "login.conf");

or with a

或与

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

Then you need a krb5 config file (usually krb5.ini or krb5.conf with correct configuration inside)

然后你需要一个 krb5 配置文件(通常是 krb5.ini 或 krb5.conf 里面有正确的配置)

If your workstation (or server) is properly configured for Kerberos this class should works as is (with propper file login.conf and krb5.ini) I used httpclient 4.3.3 and java 1.7 to test it:

如果您的工作站(或服务器)为 Kerberos 正确配置了这个类应该可以正常工作(使用 propper 文件 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");
                }

            }
        }
    }

}

Note:

笔记:

you can use:

您可以使用:

System.setProperty("sun.security.krb5.debug", "true");

or:

或者:

-Dsun.security.krb5.debug=true

to investigate problems.

调查问题。