【问题标题】:How to do a paged search on an Ldap server with lots of users?如何在有大量用户的 Ldap 服务器上进行分页搜索?
【发布时间】:2018-02-15 10:58:46
【问题描述】:

我需要创建一个使用 .NET Core 在 linux 上工作的 LDAP 客户端。我搜索了互联网,唯一支持 .Net 标准的库是 Novell.Directory.Ldap(开源,iei - https://github.com/dsbenghe/Novell.Directory.Ldap.NETStandard)。目录服务库在 .Net Core for Linux 中不支持,仅在 Windows 上支持。

我查看了文档并成功创建了一个基本的 Ldap 客户端应用程序。

现在的问题:我需要同步很多用户(10.000、200.000 个用户),默认情况下我的 ldap 服务器的最大页面大小为 1000(我不想更改它) )。我使用 VirtualListControl 来创建页面,它适用于 10k 用户。

对于 200k 用户,它会因错误 53 而崩溃 - 不愿意对 LdapSortControl 响应执行。 Novell 库需要一个 LdapSortControl 来执行分页操作(用于索引),我认为我的 ldap 无法对 200k 进行排序。我使用的代码:

        int startIndex = 1;
        int contentCount = 0;
        int afterIndex = 10;
        int count = 0;
        do
        {
            LdapVirtualListControl ctrl = new LdapVirtualListControl(startIndex, 0, afterIndex, contentCount);
            LdapSortKey[] keys = new LdapSortKey[1];
            keys[0] = new LdapSortKey("sn");
            LdapSortControl sort = new LdapSortControl(keys, true);

            LdapSearchConstraints constraints = _ldapConnection.SearchConstraints;
            constraints.setControls(new LdapControl[] { ctrl, sort});

            _ldapConnection.Constraints = constraints;
            LdapSearchResults lsc = _ldapConnection.Search(searchBase, searchScope, searchFilter, attributes, typesOnly, cons);
            while (lsc.HasMore())
            {
                try
                {
                    LdapEntry nextEntry = lsc.Next();
                    Console.WriteLine( nextEntry.DN);
                }
                catch (LdapException e)
                {
                    Console.WriteLine($"Error: {e.LdapErrorMessage}");
                    //Exception is thrown, go for next entry
                    continue;
                }

            }

            LdapControl[] controls = lsc.ResponseControls;
            if (controls == null)
            {
                Console.Out.WriteLine("No controls returned");
            }
            else
            {
                foreach (LdapControl control in controls)
                {
                    if (control.ID == "2.16.840.1.113730.3.4.10")
                    {
                        LdapVirtualListResponse response = new LdapVirtualListResponse(control.ID, control.Critical, control.getValue());
                        startIndex += afterIndex + 1;
                        contentCount = response.ContentCount;
                        count += afterIndex;
                    }
                }
            }
            Console.WriteLine(i);

        } while (count <= contentCount);

文档很小,没有足够的信息,我不知道如何以更好的方式使用 Novell 库进行分页。 这里有没有人使用 Novell Ldap 库并且有任何分页经验并且也可以帮助我?我分散了

谢谢

【问题讨论】:

    标签: c# active-directory ldap .net-core


    【解决方案1】:

    要使用 Novell.Directory.Ldap 进行分页查询,您必须使用 LdapVirtualListControl 作为“请求”控件。

    LdapVirtualListControl 尊重 Ldap 排序请求控件的参数:VLV(虚拟列表视图)是:

    before:after:index:content_count
    

    其中“before”是您希望在索引之前返回的项目数,“after”是您希望在 AFTER 之后返回的项目数索引和“content_count”是服务器中项目的预期总数。如果你不知道,你必须使用 0 作为值。

    如果要通过“ldapsearch”cli 返回前 5 个元素,则必须使用:'0:4:1:0' 和 '0:4:5:0' 用于后面的五个。

    LdapVirtualListControl 拥有一个具有相同参数但顺序不同的构造函数:

    LdapVirtualListControl(int startIndex, int beforeCount, int afterCount, int contentCount)
    

    就个人而言,我使用这个函数来正确设置参数:

    public static LdapSearchConstraints AddPagination(this LdapSearchConstraints constraints, int page,
                int pageSize)
    {
        int startIndex = (page - 1) * pageSize;
        startIndex++;
        int beforeCount = 0;
        int afterCount = pageSize - 1;
        int contentCount = 0; //0 means that i don't know the total count
    
        var lvlc = new LdapVirtualListControl(startIndex, beforeCount, afterCount, contentCount);
        constraints.setControls(lvlc);
        return constraints;
    }
    

    之后,需要注意的另一个问题是:如果您要求一组数据,这些数据位于数据集的末尾之后,您将收到数据集的第一项。

    解释:

    ldap 中存在的数据示例:
    | 1 | 2 | 3 | 4 |
    如果我们要求集合
    ________| 3 | 4 | 5 | Ldap 返回:
    ________| 3 | 4 | 1 |

    为了解决这个问题,我在返回之前删除了超出的元素:

    var lastIndex = (page * pageSize);
    if (lastIndex > result.Total)
    {
        var itemsToReturn = (int) (result.Total - (lastIndex - pageSize));
        if (itemsToReturn < 1)
        {
            items = new List<LdapQueryItem>();
        }
        else
        {
            items = items.Take(itemsToReturn).ToList();
        }
    }   
    

    最后,获取总数的函数(在 searchResults.HasMore() 方法之后执行)

    protected int? GetTotal(LdapSearchResults searchResult)
    {
        if (searchResult == null) {
           throw new ArgumentNullException(nameof(searchResult));
        }
        if (searchResult.ResponseControls != null && searchResult.ResponseControls.Any())
        {
            foreach (LdapControl control in searchResult.ResponseControls)
            {
                if (control.ID == "2.16.840.1.113730.3.4.10") // the id of the response control
                {
                    LdapVirtualListResponse response =
                        new LdapVirtualListResponse(control.ID, control.Critical, control.getValue());
                    return response.ContentCount;
                }
            }
        }
    
        return null;
    }
    

    您可以获得有关本书的更多见解和信息: Understanding and Deploying LDAP Directory Services

    【讨论】:

    • searchResult.Guard(nameof(searchResult)); 的目的是什么?我在 LdapSearchResults 中找不到 Guard 函数
    • 对不起:.Guard 扩展是“缩短”常见“ArgumentNullException”检查的扩展。您可以替换为 'if (searchResult == null) throw new ArgumentNullException(nameof(searchResult)); Ps:我现在看到有人用它做了一个库:github.com/ardalis/GuardClauses我也会更新解决方案。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2012-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-13
    • 2018-02-09
    • 1970-01-01
    • 2018-11-12
    相关资源
    最近更新 更多