通过访问 Active Directory,我能否仅从 username/email 地址获取用户的对象 GUID?

With access to Active Directory, can I get an object GUID for a user from an username/email address alone?

背景

我一直在尝试在 C# 中访问活动目录,以了解如何以各种方式 connect/validate 凭据。在此答案的底部,我包含了一些代码片段以了解我所做的事情,也许可以以此为基础来实现我的目标。

主要目标

如果我有连接到 Active Directory 的有效凭据,我可以使用一个表示 username/email 地址的字符串(假设它存在于 userPrincipalName 或类似字段中),然后取回objectGUID?

或者我是否需要考虑其他因素,例如:这些凭据必须具有的搜索其他用户的权限;了解不同广告的结构;如果 userPrincipalName 是要搜索的正确字段?

代码片段(实验性的开始,对我的目标来说功能不全)

var credentials = new NetworkCredential(username, password, hostname);
var serverId = new LdapDirectoryIdentifier(hostname);
var connection = new LdapConnection(serverId, credentials);
try
{
    connection.Bind();
}
catch (Exception e)
{
    //error
    Console.WriteLine(e);
    connection.Dispose();
    return;
}
//success

var dirEntry = new DirectoryEntry(string.Format("LDAP://{0}/{1}", hostname, baseDn), username, password);
var searcher = new DirectorySearcher(dirEntry)
{
    Filter = "(&(&(objectClass=user)(objectClass=person)))"
};
var resultCollection = searcher.FindAll();
searcher.Dispose();

DirectorySeacher 的方向是正确的。您只需要一个正确的查询,以及一些其他的调整。

  1. 修改 Filter 以便找到您要查找的内容。

    一个。如果您有电子邮件地址:

    • (&(objectClass=user)(objectClass=person)(mail=email@example.com))
    • 或者,(&(objectClass=user)(objectClass=person)(proxyAddresses=smtp:email@example.com))(这也将匹配辅助电子邮件地址)

    b。如果您有用户名,则取决于您拥有的用户名。

    • 用户主体名称:(&(objectClass=user)(objectClass=person)(userPrincipalName=username@example.com))
    • 通常称为 "username",其格式通常类似于 DOMAIN\username:(&(objectClass=user)(objectClass=person)(sAMAccountName=myusername))
  2. 使用DirectorySeacher.PropertiesToLoad。如果不这样做,它将检索每个具有值的属性,这只是浪费网络流量。

  3. 您不需要处理 DirectorySearcher,但您确实需要处理 resultCollection,因为 the documentation 表示您最终可能会发生内存泄漏如果你把它留给垃圾收集。

所以,假设你有 userPrincipalName,你会得到这样的东西:

var userToLookFor = "username@example.com";
var dirEntry = new DirectoryEntry(string.Format("LDAP://{0}/{1}", hostname, baseDn), username, password);
var searcher = new DirectorySearcher(dirEntry)
{
    Filter = $"(&(objectClass=user)(objectClass=person)(userPrincipalName={userToLookFor}))",
    SizeLimit = 1 //we're only looking for one account
};
searcher.PropertiesToLoad.Add("objectGuid");

using (var resultCollection = searcher.FindAll())
{
    if (resultCollection.Count == 1)
    {
        var userGuid = new Guid((byte[]) resultCollection[0].Properties["objectGuid"][0]);
    }
    else
    {
        //the account was not found - do something else
    }
}