跨信任域的Java AD认证Java、AD

2023-09-09 21:40:11 作者:陈南城北@

我想在Java中,将可以从Linux机器上运行,以实现Active Directory身份验证。我们的广告设置将包括多台服务器共享的的信任的相互所以对于我们的测试环境,我们有两个域控制器的关系:

I am trying to implement Active Directory authentication in Java which will be ran from a Linux machine. Our AD set-up will consist of multiple servers that share trust relationships with one another so for our test environment we have two domain controllers:

test1.ad1.foo.com 谁相信 test2.ad2.bar.com

使用code以下,我可以成功地验证从测试1 A用户,但不能在测试2

Using the code below I can successfully authenticate a user from test1 but not on test2:

public class ADDetailsProvider implements ResultSetProvider {
private String domain;
private String user;
private String password;

public ADDetailsProvider(String user, String password) {
    //extract domain name
    if (user.contains("\\")) {
        this.user = user.substring((user.lastIndexOf("\\") + 1), user.length());
        this.domain = user.substring(0, user.lastIndexOf("\\"));
    } else {
        this.user = user;
        this.domain = "";
    }

    this.password    = password;
}

    /* Test from the command line */
public static void main (String[] argv) throws SQLException {
    ResultSetProvider res = processADLogin(argv[0], argv[1]);
    ResultSet results = null;
    res.assignRowValues(results, 0);
    System.out.println(argv[0] + " " + argv[1]);
}


public boolean assignRowValues(ResultSet results, int currentRow)
    throws SQLException
{
    // Only want a single row
    if (currentRow >= 1) return false;

    try {
        ADAuthenticator adAuth = new ADAuthenticator();
        LdapContext ldapCtx = adAuth.authenticate(this.domain, this.user, this.password);
        NamingEnumeration userDetails = adAuth.getUserDetails(ldapCtx, this.user);

        // Fill the result set (throws SQLException).
        while (userDetails.hasMoreElements()) {
            Attribute attr = (Attribute)userDetails.next();
            results.updateString(attr.getID(), attr.get().toString());
        }

        results.updateInt("authenticated", 1);
        return true;

    } catch (FileNotFoundException fnf) {
        Logger.getAnonymousLogger().log(Level.WARNING,
           "Caught File Not Found Exception trying to read cris_authentication.properties");

        results.updateInt("authenticated", 0);
        return false;

    } catch (IOException ioe) {
        Logger.getAnonymousLogger().log(Level.WARNING,
            "Caught IO Excpetion processing login");

        results.updateInt("authenticated", 0);
        return false;

    } catch (AuthenticationException aex) {
        Logger.getAnonymousLogger().log(Level.WARNING,
           "Caught Authentication Exception attempting to bind to LDAP for [{0}]",
           this.user);

        results.updateInt("authenticated", 0);
        return true;

    } catch (NamingException ne) {
        Logger.getAnonymousLogger().log(Level.WARNING,
           "Caught Naming Exception performing user search or LDAP bind for [{0}]",
           this.user);
        results.updateInt("authenticated", 0);
        return true;
    }
}

public void close() {
    // nothing needed here
}

/**
 * This method is called via a Postgres function binding to access the
 * functionality provided by this class.
 */
public static ResultSetProvider processADLogin(String user, String password) {
    return new ADDetailsProvider(user, password);
}
}

public class ADAuthenticator {

public ADAuthenticator()
    throws FileNotFoundException, IOException {
    Properties props = new Properties();
    InputStream inStream = this.getClass().getClassLoader().
       getResourceAsStream("com/bar/foo/ad/authentication.properties");

    props.load(inStream);
    this.domain                = props.getProperty("ldap.domain");
    inStream.close();
}

public LdapContext authenticate(String domain, String user, String pass)
   throws AuthenticationException, NamingException, IOException {
    Hashtable env = new Hashtable();
    this.domain = domain;

    env.put(Context.INITIAL_CONTEXT_FACTORY, com.sun.jndi.ldap.LdapCtxFactory);
    env.put(Context.PROVIDER_URL, "ldap://" + test1.ad1.foo.com + ":" + 3268);
    env.put(Context.SECURITY_AUTHENTICATION, simple);
    env.put(Context.REFERRAL, follow);

    env.put(Context.SECURITY_PRINCIPAL, (domain + "\\" + user));
    env.put(Context.SECURITY_CREDENTIALS, pass);

    // Bind using specified username and password
    LdapContext ldapCtx = new InitialLdapContext(env, null);
    return ldapCtx;
}

public NamingEnumeration getUserDetails(LdapContext ldapCtx, String user)
   throws NamingException {
    // List of attributes to return from LDAP query
    String returnAttributes[] = {"ou", "sAMAccountName", "givenName", "sn", "memberOf"};

    //Create the search controls
    SearchControls searchCtls = new SearchControls();
    searchCtls.setReturningAttributes(returnAttributes);

    //Specify the search scope
    searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);

    // Specify the user to search against
    String searchFilter = "(&(objectClass=*)(sAMAccountName=" + user + "))";

    //Perform the search
    NamingEnumeration answer = ldapCtx.search("dc=dev4,dc=dbt,dc=ukhealth,dc=local", searchFilter, searchCtls);

    // Only care about the first tuple
    Attributes userAttributes = ((SearchResult)answer.next()).getAttributes();
    if (userAttributes.size() <= 0) throw new NamingException();

    return (NamingEnumeration) userAttributes.getAll();
}

据我了解的信任关系,如果 trust1 接收 trust2 尝试登录的用户,那么就应该转发尝试登录到它,它的工作原理是从用户的域名。

From what I understand of the trust relationship, if trust1 receives a login attempt for a user in trust2, then it should forward the login attempt on to it and it works this out from the user's domain name.

这是正确的还是我失去了一些东西,或者这是不可能的使用上面的方法?

Is this correct or am I missing something or is this not possible using the method above?

- EDIT-- 从LDAP绑定的堆栈跟踪 {java.naming.provider.url的= LDAP://test1.ad1.foo.com:3268,java.naming.factory.initial的= com.sun.jndi.ldap.LdapCtxFactory,java.naming .security.authentication =简单,java.naming.referral =跟随} 30 - 10月2012年13点16分02秒 ADDetailsProvider assignRowValues 警告:抓到认证异常试图绑定到LDAP进行[trusttest] 验证错误[LDAP:错误code 49 - 80090308:LdapErr:DSID-0C0903A9,注释:AcceptSecurityContext错误,数据52E,v1db0]

--EDIT-- The stack trace from the LDAP bind is {java.naming.provider.url=ldap://test1.ad1.foo.com:3268, java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory, java.naming.security.authentication=simple, java.naming.referral=follow} 30-Oct-2012 13:16:02 ADDetailsProvider assignRowValues WARNING: Caught Authentication Exception attempting to bind to LDAP for [trusttest] Auth error is [LDAP: error code 49 - 80090308: LdapErr: DSID-0C0903A9, comment: AcceptSecurityContext error, data 52e, v1db0]

推荐答案

据我所知,你应该设置Context.REFERRAL为true。 在code这是你的意思? 此外,当我切换到GSSAPI / Kerberos的, 我定义了Kerberos领域之间的信任关系,它为我工作。

As far as I know, you should set Context.REFERRAL to true. Is this what you meant in your code? In addition, when I switched to GSSAPI/Kerberos, I defined trust relationships between the kerberos realms and it worked for me.