【问题标题】:LDAP is not delivering resultsLDAP 未提供结果
【发布时间】:2016-04-22 09:20:43
【问题描述】:

我目前面临的问题是 LDAP-Query 被传递到 LDAP-Server 并且 LDAP-Server 没有提供结果。

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

给定的电子邮件是为了找不到。所以预期结果为空。

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

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

另一方面:搜索存在的电子邮件总是会立即得到结果。来自哪个主机/网络无关紧要。

LDAP 服务器是一个活动目录。有几个域控制器提供 LDAP 服务,配置为“循环”。按 ip 或 dns 访问在这个问题上没有任何区别。

通信是通过 ssl 保护的。 (ldap)

在所有情况下都建立了连接。因此,身份验证并将查询传递给 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)

有什么提示吗?

【问题讨论】:

  • 你的话有点不清楚。你是说当结果集应该为空时你永远不会得到结果?如果是这样,你怎么知道?程序永远挂起等待响应?
  • “你是说当结果集应该为空时你永远不会得到结果?”不,只有当我使用应该返回空结果的特殊查询从特殊主机查询 LDAP 服务器时。 “程序永远挂起等待响应?”正确的。但是在某些时候,由于客户端超时,客户端当然会停止请求。
  • 这台主机有什么特别之处?
  • 主机位于另一个网络 (TCP/IP)。主机运行 linux 而不是 windows(我的机器)。 Web 应用程序部署到主机上 docker 容器上的 tomcat。在我的机器上,我只使用没有 docker 环境的 tomcat。但是,如果您问,LDAP 服务器没有响应的主机有什么特别之处……这正是我的问题 :-)。
  • 你还没有展示你是如何编写查询的,或者发送它,或者结果是什么,或者异常,或者超时,...

标签: java active-directory network-programming ldap


【解决方案1】:

我们与四个人发生了什么事。我们发现,这与“跟随推荐”时服务器端的“名称解析问题”有关。客户特定的环境配置固有地禁止我们阻止这种行为。 所以我们想出了一个解决方法。

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

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

短语“不涉及引用”将我们引导至我的 JNDI 环境配置,我在其中设置了以下内容:

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

所以当我这样做时,如果查询结果似乎不完整,LDAP 服务器会询问其他 LDAP 服务器。当我开始查询时,我们的管理员确认了这一点,他们在 TCP 级别进行了调试。

不知何故,我可能没有真正正确地表示它,无法解析其他 LDAP-Server 的名称,这导致客户端在第一个 LDAP-Server 等待解析时处于饥饿状态。

我们试图省略客户端参数“Context.Refferals=follow”。在这里,我们立即得到回应。但反应并不如预期:

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

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

我们的管理员说这绝不可能,因为“每个域控制器”都是一个“全局目录服务器”。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-12
    • 1970-01-01
    • 2014-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多