【发布时间】: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