【问题标题】:How to improve performance of LDAP Search Filter for User Hierarchy如何提高用户层次结构的 LDAP 搜索过滤器的性能
【发布时间】:2021-12-27 05:04:06
【问题描述】:

我想在 LDAP 中向下搜索给定 CN 的报告层次结构,并使用 Manager 属性。任何人都可以帮助我们了解使用 Java 中的 LDAP 搜索过滤器是否有任何高效的方法来实现这一点?我尝试搜索不同的帖子,但找不到答案。

Ex:
-Dept Head(input CN) - -Level 3 User
--Manager 1 -Level 2 User
---Member 1 -Level 1 User
---Member 2 -Level 1 User
--Manager 2 -Level 2 User
---Member 3 -Level 1 User
---Member 4 -Level 1 User
--Manager 3 -Level 2 User
---Member 5 -Level 1 User
---Member 6 -Level 1 User

在上面的示例中,将输入 3 级用户 CN,我们应该检索他下面的所有用户,直到给定 CN 的 0 级(所有级别经理和成员)。

如果输入CN是1级用户CN

  • 输出将包括 1 级用户以下的所有 0 级报告(输入 CN)

如果输入CN是2级用户CN

  • 输出将包括 1 级用户(输入 CN)以下的所有 1 级和 2 级报告...等等

现在我正在为每个级别动态生成一个过滤器,并在下面的级别中使用该过滤器进行搜索。如果有更多级别,则搜索需要一些时间才能返回预期结果。

我的代码


protected static ArrayList<ValueCollection> getUserReportsTreeDetails(String accountName, String initial,
            String url, String principal, String credentials, String searchFeilds, String resultFeildNames,
            String managerAttribute) throws Exception {
        InitialLdapContext adminContext = null;

        try {
            ArrayList<SearchResult> result = new ArrayList<SearchResult>();
            Properties connectionProperties = new Properties();
            connectionProperties.put("java.naming.factory.initial", initial);
            connectionProperties.put("java.naming.provider.url", url);
            connectionProperties.put("java.naming.referral", "ignore");
            connectionProperties.put("java.naming.security.principal", principal);
            connectionProperties.put("java.naming.security.credentials", credentials);
            connectionProperties.put("javax.security.sasl.maxbuffer", 2000000);

            adminContext = new InitialLdapContext(connectionProperties, null);

            String filter;
            filter = "(&(userstatus=active)(|(" + managerAttribute + "=" + accountName + ")(CN=" + accountName + ")))";
            String searchFeildsArray[] = searchFeilds.split(",");
            String resultFeildNamesArray[] = resultFeildNames.split(",");

            NamingEnumeration<SearchResult> answers = null;
            //
            ArrayList<ValueCollection> values = new ArrayList<ValueCollection>();

            try {
                String baseDn = "ou=accounts"; // TODO: Change me
                answers = adminContext.search(baseDn, filter, getSearchControlsForFetchUser(searchFeilds));
                SearchResult userInfo;
                if (!answers.hasMore()) {
                    // throw new Exception("User does not exist in AD");
                    return null;
                }
                try {
                    String Names = "";
                    int count = 0;

                    while (answers.hasMore()) {
                        ValueCollection data = new ValueCollection();
                        userInfo = (SearchResult) answers.next();
                        for (int i = 0; i < searchFeildsArray.length; i++) {
                            Attribute attr = userInfo.getAttributes().get(searchFeildsArray[i]);
                            if (attr != null) {
                                data.put(resultFeildNamesArray[i], new StringPrimitive(attr.get().toString()));
                            }
                        }
                        values.add(data);
                        String temp = userInfo.getAttributes().get("cn").get().toString();

                        if (!temp.equals(accountName)) {
                            Names = Names == "" ? temp : Names + "," + temp;
                            count++;
                        }
                    }
                    if (Names.length() > 0)
                        values = getData(Names, managerAttribute, adminContext, baseDn, searchFeilds, resultFeildNames,
                                values);
                    return values;
                } catch (PartialResultException var10) {
                    throw new Exception("User does not exist in AD or credentials are invalid");
                }
            } finally {
                if (answers != null) {
                    answers.close();
                }
            }
        } finally {
            if (adminContext != null) {
                adminContext.close();
            }
        }
    }

    private static ArrayList<ValueCollection> getData(String accountNames, String managerAttribute,
            InitialLdapContext adminContext, String baseDn, String searchFeilds, String resultFeildNames,
            ArrayList<ValueCollection> values) throws Exception {

        String filter = "";
        String[] Names = accountNames.split(",");
        filter = "(&(userstatus=active)(|";
        for (String Name : Names) {
            filter += "(" + managerAttribute + "=" + Name + ")";
        }
        filter += "))";

        String searchFeildsArray[] = searchFeilds.split(",");
        String resultFeildNamesArray[] = resultFeildNames.split(",");
        NamingEnumeration<SearchResult> answers = null;
        // String filter = "(" + managerAttribute + "=" + accountName + ")";
        answers = adminContext.search(baseDn, filter, getSearchControlsForFetchUser(searchFeilds));
        SearchResult userInfo;
        String SearchedUsers = "";
        int count = 0;
        while (answers.hasMore()) {
            ValueCollection data = new ValueCollection();
            userInfo = (SearchResult) answers.next();
            for (int i = 0; i < searchFeildsArray.length; i++) {
                Attribute attr = userInfo.getAttributes().get(searchFeildsArray[i]);
                if (attr != null) {
                    data.put(resultFeildNamesArray[i], new StringPrimitive(attr.get().toString()));
                }
            }
            values.add(data);
            String temp = userInfo.getAttributes().get("cn").get().toString();

            SearchedUsers = SearchedUsers == "" ? temp : SearchedUsers + "," + temp;
            count++;
        }
        if (SearchedUsers.length() > 0)
            values = getData(SearchedUsers, managerAttribute, adminContext, baseDn, searchFeilds, resultFeildNames,
                    values);
        return values;
    }

【问题讨论】:

  • 您使用的是哪个 LDAP 服务器?告诉我们您尝试了什么并显示日志或结果并阅读:stackoverflow.com/help/how-to-ask
  • 部门是组织单位,经理 X 是用户吗?请更具体。我目前需要做出太多假设才能给你一个正确的答案。
  • @mvreijn 我已经更新了问题以更具体地说明需要什么
  • @jwilleke 我已经更新了问题以更具体地说明需要什么,我们正在使用提供 LDAP 服务的 Microfocus eDirectory

标签: java search filter ldap ldap-query


【解决方案1】:

恭喜!您正在使用最好的 LDAP 服务器。

我假设默认属性用于层次结构:manager 用于某人的经理,directReports 用于其下属。这些是 DN 属性,这意味着它们直接指向一个对象,您可以读取它们而不是搜索它们。

案例 1:directReports 可用

directReports 属性在组织树中指向下方。 如果填充了directReports 属性,则应按以下步骤操作:

  1. 检索指定的用户对象并检索directReports属性
  2. 对于返回的每个directReports 值,读取它指向的用户对象,并检索其directReports 属性
  3. directReports 的每个值重复步骤 2

如果您在迭代时存储每个用户,您最终会得到所需的列表。

案例 2:只有经理可用

这是一个多一点的工作。 manager 属性在组织树中指向上方。您必须搜索将给定用户作为经理的用户。

  1. 检索指定的用户对象并获取DN(通过SearchResult.getNameInNameSpace()
  2. 使用(manager=&lt;DN&gt;) 搜索用户,其中&lt;DN&gt; 是您刚刚解决的用户
  3. 对于每个结果,获取 DN 并重复步骤 2

如果您在迭代时存储每个用户,您最终会得到所需的列表。

更多指点:

  • 在您的adminContext.search() 中,baseDN 是来自directReports 的值(案例 1)或正常的基本 DN(案例 2)。
  • 在您的示例中搜索 (manager=&lt;accountname&gt;) 不会产生任何结果,因为 &lt;accountname&gt; 不是 DN 值(如 cn=accountname,ou=users,o=org

【讨论】:

  • 目前,我正在关注案例 2,因为我没有可用的 directReports 属性。我可以通过迭代每个级别来接收输出,但问题是如果有更多级别则需要花费很多时间。
  • 有一些方法可以加快速度,但最终必须执行这些搜索。需要多长时间,你有多少级别?
  • 1 级搜索最多 1000 个用户大约需要 9 秒,如果有更多用户,我需要在同一级别再次搜索“[LDAP:错误代码 80 - NDS 错误: 缓冲区不足 (-649)];"错误。所以我正在做一个拆分搜索。向上搜索服务需要更多时间的组织
  • 这种情况下-649表示搜索过滤器太大;超过 64k(这是一个相当大的搜索过滤器)。但是,一次搜索需要 9 秒的时间相当长。对我来说,这表示应该优化搜索,并且您需要在 manager 属性上建立索引
  • 是的,我的过滤器太大了,是否可以增加过滤器的大小以减少要完成的搜索次数
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-17
相关资源
最近更新 更多