如何从活动目录中获取用户列表?
Posted
技术标签:
【中文标题】如何从活动目录中获取用户列表?【英文标题】:How can I get a list of users from active directory? 【发布时间】:2011-07-06 23:33:03 【问题描述】:如何从 Active Directory 中获取用户列表?有没有办法提取用户名,名字,姓氏?我看到一个类似的帖子使用了这个:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "YOURDOMAIN");
我从来没有对活动目录做过任何事情,所以我完全迷失了。任何帮助将不胜感激!
【问题讨论】:
阅读出色的 MSDN 文章 Managing Directory Security Principals in the .NET Framework 3.5,了解如何将 AD 与 .NET 3.5 结合使用 看起来@marc_s 的文章已归档,这是updated link @marc_s 先生,我很想阅读,但链接已失效。我试过这个blogs.msdn.microsoft.com/msdnmagazine/2008/01/16/…,但即使是那篇文章上的链接也会指向微软杂志的基因页面 @Malky.Kid 我找到了这篇文章的方法。对这个问题使用link of the first comment 并下载January 2008 issue。阅读前不要忘记在资源管理器属性页面中取消阻止 chm 文件。 【参考方案1】:如果您是 Active Directory 新手,建议您先了解 Active Directory 如何存储数据。
Active Directory 实际上是一个 LDAP 服务器。存储在 LDAP 服务器中的对象是分层存储的。这与您将文件存储在文件系统中非常相似。这就是为什么它得名 Directory server 和 Active Directory
Active Directory 上的容器和对象可以由distinguished name
指定。可分辨名称是这样的CN=SomeName,CN=SomeDirectory,DC=yourdomain,DC=com
。与传统的关系数据库一样,您可以对 LDAP 服务器运行查询。这称为 LDAP 查询。
有多种方法可以在 .NET 中运行 LDAP 查询。您可以使用来自System.DirectoryServices
的DirectorySearcher 或来自System.DirectoryServices.Protocol
的SearchRequest。
对于您的问题,由于您要求专门查找用户主体对象,我认为最直观的方法是使用 System.DirectoryServices.AccountManagement
中的 PrincipalSearcher。你可以很容易地从谷歌找到很多不同的例子。这是一个完全符合您要求的示例。
using (var context = new PrincipalContext(ContextType.Domain, "yourdomain.com"))
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
foreach (var result in searcher.FindAll())
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
Console.WriteLine("First Name: " + de.Properties["givenName"].Value);
Console.WriteLine("Last Name : " + de.Properties["sn"].Value);
Console.WriteLine("SAM account name : " + de.Properties["samAccountName"].Value);
Console.WriteLine("User principal name: " + de.Properties["userPrincipalName"].Value);
Console.WriteLine();
Console.ReadLine();
请注意,在 AD 用户对象上,有许多属性。特别是,givenName
会给你First Name
,sn
会给你Last Name
。关于用户名。我认为您的意思是用户登录名。请注意,AD 用户对象上有两个登录名。一种是samAccountName
,也称为pre-Windows 2000 用户登录名。 userPrincipalName
一般在Windows 2000之后使用。
【讨论】:
如果服务器不包含域怎么办 如何使用相同的代码来列出 AD 组中的用户? 有没有办法使用这种方法将搜索范围缩小到目录中已分配电子邮件地址的人? 如果当前计算机不属于域怎么办? 我正在使用相同的方法。我正在从 AD 中获取所有用户(无过滤器)。问题是用户数量超过 150,000,因此可能需要长达 2 小时,是否有更快的方法来获取整个用户列表,或者我们是否可以优化上述示例?【参考方案2】:如果您想过滤 y 个活跃帐户,请将其添加到 Harvey's code:
UserPrincipal userPrin = new UserPrincipal(context);
userPrin.Enabled = true;
第一次使用后。然后添加
searcher.QueryFilter = userPrin;
在找到所有之前。这应该让你活跃起来。
【讨论】:
我认为您不需要searcher.QueryFilter = userPrin;
,因为我们已经在初始化时将用户主体传递给主体搜索者,但除此之外,感谢您提供仅过滤活动用户的提示!
是的,Andrey 是对的 所以基本上这可以替换为在第二个 using 语句中添加这个属性:using (var searcher = new PrincipalSearcher(new UserPrincipal(context) Enabled = true ))
但我认为您必须先测试 Enabled
是否有值:if (userPrincipal.Enabled.HasValue)
【参考方案3】:
用于浏览 AD 的 PrincipalContext 非常慢(仅将其用于 .ValidateCredentials,见下文),请改用 DirectoryEntry 和 .PropertiesToLoad(),这样您只需为所需的内容付费。
这里的过滤器和语法: https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx
这里的属性: https://docs.microsoft.com/en-us/windows/win32/adschema/attributes-all
using (var root = new DirectoryEntry($"LDAP://Domain"))
using (var searcher = new DirectorySearcher(root))
// looking for a specific user
searcher.Filter = $"(&(objectCategory=person)(objectClass=user)(sAMAccountName=username))";
// I only care about what groups the user is a memberOf
searcher.PropertiesToLoad.Add("memberOf");
// FYI, non-null results means the user was found
var results = searcher.FindOne();
var properties = results?.Properties;
if (properties?.Contains("memberOf") == true)
// ... iterate over all the groups the user is a member of
干净、简单、快速。没有魔法,没有半记录的调用 .RefreshCache 来获取 tokenGroups 或在 try/catch 中调用 .Bind 或 .NativeObject 来验证凭据。
用于验证用户:
using (var context = new PrincipalContext(ContextType.Domain))
return context.ValidateCredentials(username, password);
【讨论】:
【参考方案4】:这里的功劳当然要归功于@Harvey Kwok,但我只是想添加这个示例,因为在我的例子中,我想获得一个实际的用户主体列表。预先过滤此查询可能更有效,但在我的小型环境中,提取所有内容然后根据需要从我的列表中过滤更容易。
根据您的需要,您可能不需要强制转换为 DirectoryEntry,但某些属性在 UserPrincipal 中不可用。
using (var searcher = new PrincipalSearcher(new UserPrincipal(new PrincipalContext(ContextType.Domain, Environment.UserDomainName))))
List<UserPrincipal> users = searcher.FindAll().Select(u => (UserPrincipal)u).ToList();
foreach(var u in users)
DirectoryEntry d = (DirectoryEntry)u.GetUnderlyingObject();
Console.WriteLine(d.Properties["GivenName"]?.Value?.ToString() + d.Properties["sn"]?.Value?.ToString());
【讨论】:
请问'e'是什么? 谢谢,从来没有注意到。我改变了它,应该是“你”。如果属性丢失,我还添加了 ?s 来处理空值。【参考方案5】:包含 System.DirectoryServices.dll,然后使用下面的代码:
DirectoryEntry directoryEntry = new DirectoryEntry("WinNT://" + Environment.MachineName);
string userNames="Users: ";
foreach (DirectoryEntry child in directoryEntry.Children)
if (child.SchemaClassName == "User")
userNames += child.Name + Environment.NewLine ;
MessageBox.Show(userNames);
【讨论】:
@Fandango68:哈哈,是的! System.Windows.Forms.MessageBox.Show(ex.Message + ex.StackTrace);以上是关于如何从活动目录中获取用户列表?的主要内容,如果未能解决你的问题,请参考以下文章