从 Principal 而不是 DirectorySearcher 的 SearchResult 获取额外的 Active directory 属性

Get additional Active directory attributes from Principal instead of DirectorySearcher's SearchResult

我想从 AD 用户那里获得一些额外的 AD 属性。我想使用 PrincipalSearcher 而不是 DirectorySearcher

我所做的是获取 PrincipalSearcher 的 underlyingSearcher 并调用 FindOne() 方法。我不喜欢使用 PrincipalSearcher 中的 GetUnderlyingSearcher,但显然它有效。

代码有效,但我想知道是否有办法从 Principal

中读取额外的 properties/Ad 属性
Principal match = principalSearcher.FindOne();

另外,下面的代码可能有一些问题,因为我们正在使用 underlyingSearcher (DirectorySearcher) 方法。

我发现 PrincipalSearcher 更高级别,而且如果我要使用 DirectorySearcher,则必须定义 ldapPath e.g. var ldapPath = "DC=corp,DC=ad,DC=example,DC=com"; 它只是代码行数更少。

public AdSimpleObject GetUser(string userName, string domainName)
{
  PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, domainName);

  UserPrincipal userPrincipal = new UserPrincipal(principalContext);
  userPrincipal.SamAccountName = userName; // where condition

  using (PrincipalSearcher principalSearcher = new PrincipalSearcher(userPrincipal))
  {
    using (DirectorySearcher ds = (DirectorySearcher)principalSearcher.GetUnderlyingSearcher())
    {
      // get only properties we need, therefore search performance is increased
      ds.PropertiesToLoad.Clear();
      ds.PropertiesToLoad.AddRange(new string[]
      {
        "sAMAccountType", 
        "sAMAccountName", 
        "userPrincipalName", 
        "msDS-PrincipalName"
      });

      SearchResult match = ds.FindOne();

      if (match != null)
      {
          AdSimpleObject ado = this.CreateAdSimpleObject(match);
          return ado;
      }  
      return null;
    }
  }
}

public class AdSimpleObject
{
  public string AdsPath { get; set; }
  public int SamAccountType { get; set; }
  public string SamAccountName { get; set; }
  public string MsDsPrincipalName { get; set; }
  public string UserPrincipalName { get; set; }
}

private AdSimpleObject CreateAdSimpleObject(SearchResult searchItem)
{
  ResultPropertyCollection props = searchItem.Properties;

  string adsPath = props["adspath"]?.OfType<string>().ToList().FirstOrDefault() ?? string.Empty;
  int samAccountType = props["sAMAccountType"]?.OfType<int>().ToList().FirstOrDefault() ?? 0;
  string samAccountName = props["sAMAccountName"]?.OfType<string>().ToList().FirstOrDefault() ?? string.Empty;
  string userPrincipalName = props["userPrincipalName"]?.OfType<string>().ToList().FirstOrDefault() ?? string.Empty;
  string msDsPrincipalName = props["msDS-PrincipalName"]?.OfType<string>().ToList().FirstOrDefault() ?? string.Empty;

  return new AdSimpleObject
  {
      AdsPath = adsPath,
      SamAccountType = samAccountType,
      SamAccountName = samAccountName,
      UserPrincipalName = userPrincipalName,
      MsDsPrincipalName = msDsPrincipalName
  };
}

I'm wondering if there is a way to read additional properties/Ad attributes from Principal

您将使用 DirectoryEntry object returned from Principal.GetUnderlyingObject:

Principal match = principalSearcher.FindOne();
var de = (DirectoryEntry) match.GetUnderlyingObject();
de.RefreshCache(new string[] {"someAttribute"});
var someAttribute = de.Properties["someAttribute"].Value;

这样做将返回到 AD 以获取属性,而不是使用在搜索中检索到的数据。 RefreshCache的使用是告诉它只获取你感兴趣的属性。否则,如果你马上使用DirectoryEntry.Properties,它会出去到AD并获取所有属性有一个值,这不太可能是你需要的,而且会无缘无故地花费额外的时间。

Additionally could below code have some problems because we are using the underlyingSearcher (DirectorySearcher) methods.

完全没有。该代码与制作您自己的 DirectorySearcher 并使用它没有什么不同。事实上,按照您编写这段代码的方式,根本没有必要使用 UserPrincipal/PrincipalSearcher。它为您做的唯一一件事就是构建查询字符串。

整个 AccountManagement 命名空间只是 DirectoryEntry/DirectorySearcher 的包装器。它使程序员的事情变得更容易(在某些情况下,并非全部),但这样做是以牺牲性能为代价的。

如果您自己直接使用 DirectoryEntry/DirectorySearcher,您可以更好地控制从 AD 检索的数据量以及网络请求的频率。这些转化为更少的时间。如果您只是搜索一个帐户,那么它不会有太大的不同。但是,如果您要搜索大量用户,或者遍历大量用户列表,则效果会大不相同。

我写了一篇关于在与 AD 交谈时优化性能的文章(特别是 DirectoryEntry/DirectorySearcher):Active Directory: Better performance