跨受信任域的Java AD身份验证

跨受信任域的Java AD身份验证,java,active-directory,ldap,Java,Active Directory,Ldap,我正在尝试用Java实现Active Directory身份验证,它将从Linux机器上运行。我们的AD设置将由多个彼此共享信任关系的服务器组成,因此对于我们的测试环境,我们有两个域控制器: test1.ad1.foo.com谁信任test2.ad2.bar.com 使用下面的代码,我可以从test1成功验证用户身份,但不能在test2上验证用户身份: public class ADDetailsProvider implements ResultSetProvider { private St

我正在尝试用Java实现Active Directory身份验证,它将从Linux机器上运行。我们的AD设置将由多个彼此共享信任关系的服务器组成,因此对于我们的测试环境,我们有两个域控制器:

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

使用下面的代码,我可以从
test1
成功验证用户身份,但不能在
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();
}
公共类AddTailsProvider实现ResultsProvider{
私有字符串域;
私有字符串用户;
私有字符串密码;
公共ADDetailsProvider(字符串用户、字符串密码){
//提取域名
if(user.contains(“\\”){
this.user=user.substring((user.lastIndexOf(“\\”)+1),user.length();
this.domain=user.substring(0,user.lastIndexOf(“\\”);
}否则{
this.user=用户;
this.domain=“”;
}
this.password=密码;
}
/*从命令行进行测试*/
公共静态void main(字符串[]argv)引发SQLException{
ResultSetProvider res=processADLogin(argv[0],argv[1]);
ResultSet results=null;
res.AssignRowValue(结果,0);
System.out.println(argv[0]+“”+argv[1]);
}
公共布尔赋值行值(结果集结果,int currentRow)
抛出SQLException
{
//我只想要一排
如果(currentRow>=1)返回false;
试一试{
ADAuthenticator adAuth=新的ADAuthenticator();
LdapContext ldapCtx=adAuth.authenticate(this.domain、this.user、this.password);
namingumerationuserdetails=adAuth.getUserDetails(ldapCtx,this.user);
//填充结果集(抛出SQLException)。
while(userDetails.hasMoreElements()){
属性attr=(属性)userDetails.next();
updateString(attr.getID(),attr.get().toString());
}
结果。更新(“已验证”,1);
返回true;
}捕获(FileNotFoundException fnf){
Logger.getAnonymousLogger().log(Level.WARNING,
“捕获的文件未找到尝试读取cris_authentication.properties时出现异常”);
结果。更新(“已验证”,0);
返回false;
}捕获(ioe异常ioe){
Logger.getAnonymousLogger().log(Level.WARNING,
“捕获IO执行处理登录”);
结果。更新(“已验证”,0);
返回false;
}捕获(AuthenticationException aex){
Logger.getAnonymousLogger().log(Level.WARNING,
“捕获到尝试绑定到[{0}]的LDAP的身份验证异常”,
这是(用户);
结果。更新(“已验证”,0);
返回true;
}捕获(纳明异常){
Logger.getAnonymousLogger().log(Level.WARNING,
“在对[{0}]执行用户搜索或LDAP绑定时发现命名异常”,
这是(用户);
结果。更新(“已验证”,0);
返回true;
}
}
公众假期结束(){
//这里什么都不需要
}
/**
*此方法通过Postgres函数绑定调用,以访问
*此类提供的功能。
*/
公共静态结果提供程序processADLogin(字符串用户,字符串密码){
返回新的ADDetailsProvider(用户、密码);
}
}
公共类身份验证程序{
公共身份验证程序()
抛出FileNotFoundException,IOException{
Properties props=新属性();
InputStream inStream=this.getClass().getClassLoader()。
getResourceAsStream(“com/bar/foo/ad/authentication.properties”);
支柱荷载(河道内);
this.domain=props.getProperty(“ldap.domain”);
流内关闭();
}
公共LdapContext身份验证(字符串域、字符串用户、字符串传递)
抛出AuthenticationException、NamingException、IOException{
Hashtable env=新的Hashtable();
this.domain=域;
put(Context.INITIAL\u Context\u工厂,com.sun.jndi.ldap.LdapCtxFactory);
env.put(Context.PROVIDER_URL,“ldap://”+test1.ad1.foo.com+“:”+3268);
环境put(Context.SECURITY\u身份验证,简单);
环境投入(上下文、参考、遵循);
环境put(Context.SECURITY\u主体,(域+“\\”+用户));
环境put(Context.SECURITY\u凭证,pass);
//使用指定的用户名和密码绑定
LdapContext ldapCtx=新的初始LdapContext(env,null);
返回ldapCtx;
}
公共NamingEnumeration getUserDetails(LdapContext ldapCtx,字符串用户)
抛出NamingException{
//要从LDAP查询返回的属性列表
字符串returnAttributes[]={“ou”、“sAMAccountName”、“givenName”、“sn”、“memberOf”};
//创建搜索控件
SearchControls searchCtls=新的SearchControls();
searchCtls.setReturningAttributes(returnAttributes);
//指定搜索范围
searchCtls.setSearchScope(SearchControls.SUBTREE_范围);
//指定要搜索的用户
字符串searchFilter=“(&(objectClass=*)(sAMAccountName=“+user+”)”);
//执行搜索
NamingEnumeration answer=ldapCtx.search(“dc=dev4,dc=dbt,dc=ukhealth,dc=local”,searchFilter,searchCtls);
//只关心第一个元组
Attributes userAttributes=((SearchResult)answer.next()).getAttributes();

据我所知,如果(userAttributes.size(),您应该将Context.reference设置为true。
这就是您在代码中的意思吗?
此外,当我切换到GSSAPI/Kerberos时,

我定义了kerberos领域之间的信任关系,它对我很有用。

我在阅读本指南时设置了遵循的方法,我确实尝试将其设置为true,但这会引发命名异常。感谢您的回复!将引用方法设置为true的堆栈宽限值是
javax.Naming.NoInitialContextException:无法更改使用哈希表中指定的工厂创建InitialContext[根异常为java.lang.IllegalArgumentException:java.naming.referral属性的非法值。]
谢谢