Java JNDI API用户无法通过多林设置对AD进行身份验证

时间:2018-07-06 21:35:08

标签: java active-directory jndi

在测试环境中,我设置了两个活动目录目录林A和B,每个目录林都有一个域控制器,并且目录林具有两种方式的信任设置。

我有用户-forestA中的userA和forestB中的userB。

我已经使用adfind.exe来测试userA和userB是否可以通过击中forestA端点进行身份验证,例如 。\ AdFind.exe -h -u userA@forestA.com -up 。\ AdFind.exe -h -u userB@forestA.com -up

两个用户都可以按照预期的方式通过forestA进行身份验证,因为他们具有两种方式的信任设置。

但是,userB永远无法使用Java JNDI API进行身份验证。我一直在搜索,发现最多的是他们建议对Context.SECURITY_PRINCIPAL属性使用DN或UPN,但它们都不适合我。

我非常确定原理和凭据正确无误,因为如果将主机更改为存储用户的森林B,则userB可以很好地进行身份验证。

这是我使用的标准JNDI代码-

Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://forestA.com");
//env.put(Context.SECURITY_PRINCIPAL, "userB@forestB.com");
//env.put(Context.SECURITY_PRINCIPAL, 
    CN=userB,OU=marketing,DC=forestB,DC=com");
env.put(Context.SECURITY_PRINCIPAL, "forestB\\userB");
env.put(Context.SECURITY_CREDENTIALS, "mypass");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.REFERRAL, "follow");

DirContext dirContext = new InitialDirContext(env);

我已经坚持了好几天,感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

经过几天的研究和深入研究,我终于找到了解决方案。希望这对那些像我一样挣扎的人是有益的:) ...

首先,让我纠正上面使用的adfind示例,那里有一个错字,应该是以下内容-

.\AdFind.exe -h forestA.com -u userA@forestA.com -up pass
.\AdFind.exe -h forestA.com -u userB@forestB.com -up pass

两个adfind命令都会按预期通过。但是,

.\AdFind.exe -h forestA.com -u userB@forestB.com -up pass -simple

这将失败,因为它被迫使用“简单”身份验证。这让我想到,默认情况下,adfind使用的是其他方式,例如Kerberos身份验证,“简单”不适用于跨林身份验证。

我开始将Java JNDI代码从“简单”更改为Kerberos(GSSAPI),并且有效! 确保您的领域名称使用大写字母,并且ldap URL必须是主机名。我也想让它也能正常工作。

我已经包含了所有示例代码和配置文件,它们是使它正常工作所需要的。

JndiAction.java

public class JndiAction implements java.security.PrivilegedAction {
    private String url;
    private DirContext ctx;

    public Object run() {
        performJndiOperation();
        return null;
    }

    private void performJndiOperation(/*String[] args*/) {
        // Set up environment for creating initial context
        Hashtable env = new Hashtable(11);

        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://server01.forestb.com");
        // Request the use of the "GSSAPI" SASL mechanism
        // Authenticate by using already established Kerberos credentials
        env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");

        try {
            /* Create initial context */
            ctx = new InitialDirContext(env);
            System.out.println("Authenticated!");

            // Close the context when we're done
            ctx.close();
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }
}

GssExample.java

public class GssExample {
    public static void main(String[] args) {
        System.setProperty("sun.security.debug", "true");
        System.setProperty("sun.security.krb5.debug", "true");
        System.setProperty("java.security.krb5.conf", "C:\\dev\\active_directory\\krb5.conf");
        System.setProperty("java.security.auth.login.config", "C:\\dev\\active_directory\\jaas.conf");

        // 1. Log in (to Kerberos)
        LoginContext lc = null;
        try {
            SampleCallbackHandler cbHandler = new SampleCallbackHandler();
            cbHandler.setUsername("userb@FORESTB.COM"); 
            cbHandler.setPassword("mypass");
            lc = new LoginContext(GssExample.class.getName(), cbHandler);

            // Attempt authentication
            lc.login();
        } catch (Exception le) {
            le.printStackTrace();
            System.exit(-1);
        }

        // 2. Perform JNDI work as logged in subject
        Subject.doAs(lc.getSubject(), new JndiAction());
    }
}  

SampleCallbackHandler.java

public class SampleCallbackHandler implements CallbackHandler {
    private String username = "";
    private String password = "";

    public void handle(Callback[] callbacks)
        throws java.io.IOException, UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
            if (callbacks[i] instanceof NameCallback) {
                NameCallback cb = (NameCallback)callbacks[i];
                cb.setName(this.username);

            } else if (callbacks[i] instanceof PasswordCallback) {
                PasswordCallback cb = (PasswordCallback)callbacks[i];
                String pw = this.password; 
                char[] passwd = new char[pw.length()];
                pw.getChars(0, passwd.length, passwd, 0);
                cb.setPassword(passwd);
            } else {
                throw new UnsupportedCallbackException(callbacks[i]);
            }
        }
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

krb5.conf

[libdefaults]
    default_realm = FORESTB.COM
[realms]
    FORESTA.COM  = {
           kdc = foresta.com
    }
    FORESTB.COM  = {
           kdc = forestb.com
    }

jaas.conf

GssExample {
    com.sun.security.auth.module.Krb5LoginModule required
      client=TRUE
      debug=true
      useTicketCache=true
      principal=david;
};