LDAP无法提供结果

时间:2016-04-22 09:20:43

标签: java active-directory network-programming ldap

我目前面临的问题是LDAP-Query传递给LDAP服务器而LDAP服务器不会传递结果。

查询:(&(objectCategory = user)(mail =tester@oop-expert.de))

无法找到给定的电子邮件。所以预计会有空结果。

在大多数环境配置中,此查询将完美传递,LDAP会立即返回空结果。

我将问题分解为可能与发送查询的网络或主机有关的问题。因此,如果从一个主机/网络和另一个主机/网络发送LDAP-Server将“饿死”我的LDAP客户端,那么查询将会很好,因此LDAP客户端会关闭连接以进行客户端超时。

另一方面:搜索存在的电子邮件将始终导致立即结果。与哪个主机/网络无关。

LDAP-Server是一个Active Directory。有几个域控制器提供LDAP服务,配置“循环”。每个ip或dns访问对此主题没有任何影响。

通过ssl保护通信。 (LDAPS)

在所有情况下都建立了连接。因此身份验证并将查询传递给LDAP就可以了。

授权不应成为问题。我在所有情况下使用相同的LDAP用户。

LDAP客户端始终是使用InitialContext的JAVA实现。

private InitialDirContext createDirContext(String principal, String credentials) throws NamingException {

    if (credentials == null || credentials.isEmpty()) {
        throw new LDAPLoginException();
    }

    return new InitialDirContext(createEnvironment(principal, credentials));
}


private Hashtable<String, String> createEnvironment(String principal, String credentials) {

    Hashtable<String, String> env = new Hashtable<>();

    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, this.ldapUrl);

    // To get rid of the PartialResultException when using Active Directory
    env.put(Context.REFERRAL, "follow");

    // Needed for the Bind (User Authorized to Query the LDAP server)
    env.put(Context.SECURITY_AUTHENTICATION, "simple");

    env.put(Context.SECURITY_PRINCIPAL, principal);
    env.put(Context.SECURITY_CREDENTIALS, credentials);

    return env;
}

构建和执行查询的代码:

private LDAPUser getLDAPUserInfoByUniqueField(String attr, String value) {
    DirContext serviceUserContext = null;
    NamingEnumeration<SearchResult> results = null;

    try {

        String searchString = "(&(objectCategory=user)(" + attr + "=" + value + "))";

        serviceUserContext = createDirContext(this.serviceUserPrincipal, serviceUserCredentials);
        results = serviceUserContext.search("", searchString, createSearchControls()); // blocking...

        return createLDAPUserInfo(results);

    } catch (LDAPLoginException e) {
        throw e;
    } catch (Exception e) {
        throw new LDAPFatalException(e);
    } finally {

        try {
            if (results != null)
                results.close();
        } catch (NamingException e) {
        }

        try {
            if (serviceUserContext != null)
                serviceUserContext.close();
        } catch (NamingException e) {
        }

    }
}

实用方法:

private LDAPUser createLDAPUserInfo(NamingEnumeration<SearchResult> results) throws NamingException {
    LDAPUser ldapUserInfo = null;

    if (results.hasMore()) { // blocking here

        SearchResult result = (SearchResult) results.next();

        String sAMAccountName = extractsAMAccountName(result);
        String distinguishName = extractDistinguishName(result);
        String department = extractDepartment(result);
        String email = extractEmail(result);

        ldapUserInfo = new LDAPUser(sAMAccountName, distinguishName, department, email);

    }
    return ldapUserInfo;
}


private String extractsAMAccountName(SearchResult result) throws NamingException {
    Attributes attrs = result.getAttributes();
    Attribute attr = attrs.get("sAMAccountName");
    return (String) attr.get();
}

例外:

de.oopexpert.business.ldap.LDAPFatalException: javax.naming.PartialResultException [Root exception is javax.naming.CommunicationException: oopexpert.de:636 [Root exception is java.net.ConnectException: Connection timed out]]
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByUniqueField(LDAPImpl.java:90)
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByEmail(LDAPImpl.java:57)
Caused by: javax.naming.PartialResultException [Root exception is javax.naming.CommunicationException: oopexpert.de:636 [Root exception is java.net.ConnectException: Connection timed out]]
at com.sun.jndi.ldap.LdapNamingEnumeration.hasMoreImpl(LdapNamingEnumeration.java:242)
at com.sun.jndi.ldap.LdapNamingEnumeration.hasMore(LdapNamingEnumeration.java:189)
at de.oopexpert.business.ldap.impl.LDAPImpl.createLDAPUserInfo(LDAPImpl.java:139)
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByUniqueField(LDAPImpl.java:84)

任何提示?

1 个答案:

答案 0 :(得分:1)

我们与四个人一起努力。我们发现,当“关注refferals”时,它与服务器端的“名称解析问题”有关。客户特定的环境配置固有地禁止我们阻止此行为。 所以我们想出了一个解决方法。

由于我们在活动目录域中运行,因此我们拥有全局编录服务器。对此的重要声明来自Microsoft“technet”:

  

全局编录是一个分布式数据存储库,其中包含多域Active Directory域服务(AD DS)林中每个域中每个对象的可搜索部分表示形式。全局编录存储在已指定为全局编录服务器的域控制器上,并通过多主机复制进行分发。定向到全局编录的搜索更快,因为它们不涉及对不同域控制器的引用。   (来自What Is the Global Catalog?

“不涉及refferals”这一短语将我们带到了我的JNDI环境配置,我在其中设置了以下内容:

env.put(Context.REFERRAL, "follow");

因此,当我这样做时,如果查询的结果似乎不完整,LDAP-Server将询问其他LDAP服务器。当我启动查询并在TCP级别上调试它时,我们的管理员确认了这一点。

不知何故,我可能没有真正正确地表示它,其他LDAP服务器的名称无法解决,导致客户端因为第一个LDAP服务器等待解决而导致饥饿。

我们尝试省略客户端参数“Context.Refferals = follow”。在这里我们立即得到回应。但回应并不像预期的那样:

javax.naming.PartialResultException: Unprocessed Continuation Reference(s); remaining name ''

这是因为第一个LDAP-Server有意见返回不完整的数据。

我们的管理员说,情况永远不会如此,因为“每个域控制器”都是“全局编录服务器”。

所以我的解决方法是:我将处理此异常并将其解释为“无结果”。