【问题标题】:Getting all direct Reports from Active Directory从 Active Directory 获取所有直接报告
【发布时间】:2023-03-08 12:08:02
【问题描述】:

我正在尝试以递归方式通过 Active Directory 获取用户的所有直接报告。 因此,给定一个用户,我最终会得到一个列表,其中列出了将这个人作为经理或将一个人作为经理、将一个人作为经理......最终将输入用户作为经理的所有用户。

我目前的尝试很慢:

private static Collection<string> GetDirectReportsInternal(string userDN, out long elapsedTime)
{
    Collection<string> result = new Collection<string>();
    Collection<string> reports = new Collection<string>();

    Stopwatch sw = new Stopwatch();
    sw.Start();

    long allSubElapsed = 0;
    string principalname = string.Empty;

    using (DirectoryEntry directoryEntry = new DirectoryEntry(string.Format("LDAP://{0}",userDN)))
    {
        using (DirectorySearcher ds = new DirectorySearcher(directoryEntry))
        {
            ds.SearchScope = SearchScope.Subtree;
            ds.PropertiesToLoad.Clear();
            ds.PropertiesToLoad.Add("directReports");
            ds.PropertiesToLoad.Add("userPrincipalName");
            ds.PageSize = 10;
            ds.ServerPageTimeLimit = TimeSpan.FromSeconds(2);
            SearchResult sr = ds.FindOne();
            if (sr != null)
            {
                principalname = (string)sr.Properties["userPrincipalName"][0];
                foreach (string s in sr.Properties["directReports"])
                {
                    reports.Add(s);
                }
            }
        }
    }

    if (!string.IsNullOrEmpty(principalname))
    {
        result.Add(principalname);
    }

    foreach (string s in reports)
    {
        long subElapsed = 0;
        Collection<string> subResult = GetDirectReportsInternal(s, out subElapsed);
        allSubElapsed += subElapsed;

        foreach (string s2 in subResult)
        {
        result.Add(s2);
        }
    }



    sw.Stop();
    elapsedTime = sw.ElapsedMilliseconds + allSubElapsed;
    return result;
}

本质上,此函数将可分辨名称作为输入(CN=Michael Stum,OU=test,DC=sub,DC=domain,DC=com),因此,对 ds.FindOne() 的调用很慢.

我发现搜索 userPrincipalName 要快得多。我的问题:sr.Properties["directReports"] 只是一个字符串列表,这就是 distinctName,搜索起来似乎很慢。

我想知道,有没有一种快速的方法可以在 distinctName 和 userPrincipalName 之间进行转换?或者,如果我只有 distinctName 可以使用,是否有更快的方法来搜索用户?

编辑:感谢您的回答!搜索经理字段将功能从 90 秒提高到 4 秒。这是新的和改进的代码,它更快、更易读(请注意,elapsedTime 功能中很可能存在错误,但函数的实际核心工作):

private static Collection<string> GetDirectReportsInternal(string ldapBase, string userDN, out long elapsedTime)
{
    Collection<string> result = new Collection<string>();

    Stopwatch sw = new Stopwatch();
    sw.Start();
    string principalname = string.Empty;

    using (DirectoryEntry directoryEntry = new DirectoryEntry(ldapBase))
    {
        using (DirectorySearcher ds = new DirectorySearcher(directoryEntry))
        {
            ds.SearchScope = SearchScope.Subtree;
            ds.PropertiesToLoad.Clear();
            ds.PropertiesToLoad.Add("userPrincipalName");
            ds.PropertiesToLoad.Add("distinguishedName");
            ds.PageSize = 10;
            ds.ServerPageTimeLimit = TimeSpan.FromSeconds(2);
            ds.Filter = string.Format("(&(objectCategory=user)(manager={0}))",userDN);

            using (SearchResultCollection src = ds.FindAll())
            {
                Collection<string> tmp = null;
                long subElapsed = 0;
                foreach (SearchResult sr in src)
                {
                    result.Add((string)sr.Properties["userPrincipalName"][0]);
                    tmp = GetDirectReportsInternal(ldapBase, (string)sr.Properties["distinguishedName"][0], out subElapsed);
                    foreach (string s in tmp)
                    {
                    result.Add(s);
                    }
                }
            }
          }
        }
    sw.Stop();
    elapsedTime = sw.ElapsedMilliseconds;
    return result;
}

【问题讨论】:

  • 您可以通过将 DirectoryEntry 和 DirectorySearcher 排除在递归之外来获得额外的速度。它们之间不会发生变化,对吗?
  • 不再。我没有说的是:我在 Sharepoint 环境中使用它,其中调用包含在 SPSecurity.RunWithElevatedPrivileges 调用中,这意味着 ref 参数是不可能的,我不确定是否将它作为普通参数传递(奇怪Sharepoint 安全性)
  • 我认为应该可以。对象始终作为 ref 传递,AFAIK。见:stackoverflow.com/questions/186891/…
  • 一个关于绕过共享点安全怪异的想法。为什么不在 SP 之外的 Web 服务中托管此功能,然后将代码放入共享点以访问此服务。

标签: c# active-directory ldap


【解决方案1】:

首先,当您已经拥有要查找的 DN 时,无需将 Scope 设置为“子树”。

另外,如何查找“manager”属性是您要查找的人的所有对象,然后对其进行迭代。这通常应该比其他方式更快。

(&(objectCategory=user)(manager=<user-dn-here>))

编辑:以下内容很重要,但到目前为止只在 cmets 中提到过这个答案:

如上所示构建过滤器字符串时,存在使用对 DN 有效但在过滤器中具有特殊含义的字符破坏它的风险。这些must be escaped

*   as  \2a
(   as  \28
)   as  \29
\   as  \5c
NUL as  \00
/   as  \2f

// Arbitrary binary data can be represented using the same scheme.

编辑:将SearchRoot 设置为对象的DN,将SearchScope 设置为Base 也是将单个对象拉出AD 的快速方法。

【讨论】:

  • 谢谢。我将看看它在没有 Subtree 的情况下如何执行。对于您的第二个建议,这听起来很有趣。我需要稍微思考一下,因为该函数仍然需要递归,但我会立即对其进行测试。
  • 如果我能投票给你 10 次,我会这样做。更改搜索经理的功能将其从运行 90 秒改进为现在仅 4 秒。
  • 请注意,使用这种方法,您需要转移使用在 DN 中有效但在过滤器字符串中保留的字符破坏过滤器字符串的风险。在我的头顶上,至少需要转义“#”。
  • 感谢您提供的信息。我现在在一本书中找到了这一点:“字符 不能出现在 DN 中,除非它们用 \ 字符进行转义。此外,如果 # 是 DN 中的第一个字符,则必须对其进行转义。 "
  • 实际上,我认为我看错了来源。返回的 DN 将始终为 DN 正确转义,但您指的是过滤器:msdn.microsoft.com/en-us/library/aa746475.aspx => "Special Characters"。然后,我将在我的代码中添加一个“Escape DN for Filter”功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-26
相关资源
最近更新 更多