2009-05-05 77 views
18

我想弄清楚如何从C#中搜索AD,类似于“查找用户,联系人和组”在Active Directory用户和计算机工具中的工作原理。我有一个包含组名或用户名的字符串(通常以firstname middleinitial [如果他们有一个] lastname的格式,但并不总是)。即使我为组和用户分别进行查询,我也无法想出一种搜索方式来捕获大多数用户帐户。 “查找用户”,“联系人”和“组”工具几乎每次都会将其返回。任何人有任何建议?如何从C#中找到活动目录中的用户?

我已经知道如何使用DirectorySearcher类,问题是我无法找到我想要的查询。 cn和samaccount的名字都没有与这个用户的名字有关,所以我无法搜索这些。将事件分解并搜索sn和givenName并不会像工具那样接近任何地方。

+0

下面的答案都是关于使用sAMAccountName赋,我不知道他们为什么有这么多的upvotes。这个问题是关于使用名字和姓氏来获取属性!只有顶部/标记的正确答案更接近。 – vapcguy 2016-11-03 17:04:12

回答

18

您在.NET 3 .5?如果是这样 - AD在.NET 3.5中有很好的新功能 - 请查看Ethan Wilanski和Joe Kaplan撰写的这篇文章Managing Directory Security Principals in .NET 3.5

其中一个重要的新功能是“PrincipalSearcher”类,它应该大大简化在AD中查找用户和/或组的过程。

如果您不能使用.NET 3.5,可能会让您的生活更轻松的一件事就是所谓的“不明确的名称解析”,它是一个鲜为人知的特殊搜索过滤器,它将一次搜索任何与名称相关的属性。

指定您的LDAP搜索查询是这样的:

searcher.Filter = string.Format("(&(objectCategory=person)(anr={0}))", yourSearchTerm) 

此外,我建议对“objectCategory”属性过滤的,因为这是单值,并在公元默认索引,这是一个很大的速度比使用“objectClass”。

马克

10

System.DirectoryServices有两个名称空间... DirectoryEntry和DirectorySearcher。在这里的DirectorySearcher

更多信息:

http://msdn.microsoft.com/en-us/library/system.directoryservices.directorysearcher.aspx

然后,您可以使用筛选器属性由集团用户等进行过滤...

所以,如果你想按帐户名称进行筛选您可以将.Filter设置为:

"(&(sAMAccountName=bsmith))" 

并运行FilterAll方法。这将返回一个SearchResultCollection,您可以循环并提取有关该用户的信息。

+0

谢谢,虽然我知道DirectorySearcher。问题是我无法想出一个查询来查找AD中的用户。 – Sunookitsune 2009-05-05 15:05:50

+0

@Sunookitsune - 所以你试图复制“查找用户,联系人和组”的确切功能? – 2009-05-05 15:45:04

+0

@Miyagi Coder我不会说完全的功能,但至少可以找到接近搜索用户的东西。或者找一些能工作的东西,因为我很难过。 – Sunookitsune 2009-05-05 15:48:13

4

您需要根据您如何查找用户来构建搜索字符串。

using (var adFolderObject = new DirectoryEntry()) 
{ 
    using(var adSearcherObject = new DirectorySearcher(adFolderObject)) 
    { 
      adSearcherObject.SearchScope = SearchScope.Subtree; 
      adSearcherObject.Filter = "(&(objectClass=person)(" + userType + "=" + userName + "))"; 

      return adSearcherObject.FindOne(); 
    } 
} 

根据用户名格式的不同,userType应该是sAMAccountName或CN。

例如:
firstname.lastname(或flastname)通常是sAMAccountName赋
名姓通常会是CN

+0

不,`sAMAccountName`是用户名,即John Doe的`doej`,并且OP没有用户名。如果你要为`userType`使用`CN`并且放入`FirstName LastName`而不是`username`,那么使用过滤器`“(&(objectCategory = user)(objectClass = user)( givenName =“+ firstName +”)(sn =“+ lastName +”))“;”代替。 – vapcguy 2016-11-03 18:32:52

2

要添加到宫城的答案.​​...

这里有一个过滤器/查询申请的DirectorySearcher

DirectorySearcher ds = new DirectorySearcher(); 

ds.Filter = "samaccountname=" + userName; 

SearchResult result = ds.FindOne(); 
3
public DirectoryEntry Search(string searchTerm, string propertyName) 
{ 
    DirectoryEntry directoryObject = new DirectoryEntry(<pathToAD>); 

    foreach (DirectoryEntry user in directoryObject.Children) 
    { 
     if (user.Properties[propertyName].Value != null)  
     if (user.Properties[propertyName].Value.ToString() == searchTerm) 
      return user;      
    } 

    return null; 
} 
3

Joe Kaplan and Ethan Wilansky文章 使用此应用得到这个(参考上System.DirectoryServices.AccountManagement DLL):

using System.DirectoryServices.AccountManagement; 

private bool CheckUserinAD(string domain, string username) 
{ 
    PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, domain); 
    UserPrincipal user = new UserPrincipal(domainContext); 
    user.Name = username; 
    PrincipalSearcher pS = new PrincipalSearcher(); 
    pS.QueryFilter = user; 
    PrincipalSearchResult<Principal> results = pS.FindAll(); 
    if (results != null && results.Count() > 0) 
     return true; 
    return false; 
} 
0

其他的答案被描述甚少,也没有描述如何实施它们,并且大多数给出了错误的过滤器属性。您甚至不需要使用.Filter - 只需将您的属性(姓氏= .Surname,名字= .GivenName)分配给UserPrincipal对象,然后在任何触发搜索的事件中使用PrincipalSearcher在该对象上搜索:

string firstName = txtFirstName.Text; 
string lastName = txtLastName.Text; 

PrincipalContext ctx = new PrincipalContext(ContextType.Domain); 

UserPrincipal up = new UserPrincipal(ctx); 
if (!String.IsNullOrEmpty(firstName)) 
    up.GivenName = firstName; 
if (!String.IsNullOrEmpty(lastName)) 
    up.Surname = lastName; 

PrincipalSearcher srch = new PrincipalSearcher(up); 
srch.QueryFilter = up; 

我假设你有一个名字和姓氏的文本框得到它,用txtFirstNametxtLastName IDS /名称。请注意,如果您要查找的属性中没有值,请勿将其添加到UserPrincipal,否则会导致异常。这就是我上面列出的支票的原因。

你那么做srch一个.FindAll得到的搜索结果为PrincipalSearchResult集合Principal对象:

using (PrincipalSearchResult<Principal> results = srch.FindAll()) 
{ 
    if (results != null) 
    { 
     int resultCount = results.Count(); 
     if (resultCount > 0) // we have results 
     { 
      foreach (Principal found in results) 
      { 
       string username = found.SamAccountName; // Note, this is not the full user ID! It does not include the domain. 
      } 
     } 
    } 
} 

请注意,即使其.Count()0,为什么两个检查结果都不会是空那里。

您使用该迭代使用该foreach来获取所需的属性,这回答了如何使用C#在AD中查找用户的问题,但请注意,只能使用Principal对象获得几个属性,如果我通过谷歌(如我一样)达成这个问题,我会非常沮丧。如果你发现这就是你需要的 - 很好,你完成了!但为了获得休息(并且保持良知),你必须下潜,然后我会介绍如何做到这一点。

我发现你不能只用username我把上面的,但你必须得到整个DOMAIN\doej种的名字。这是你如何做到的。相反,把这个在foreach环,上面:

string userId = GetUserIdFromPrincipal(found); 

,利用此功能:

private static string GetUserIdFromPrincipal(Principal prin) 
{ 
    string upn = prin.UserPrincipalName; 
    string domain = upn.Split('@')[1]; 
    domain = domain.Substring(0, domain.IndexOf(".YOURDOMAIN")); 

    // "domain" will be the subdomain the user belongs to. 
    // This may require edits depending on the organization. 

    return domain + @"\" + prin.SamAccountName; 
} 

一旦你有,你可以调用这个函数:

public static string[] GetUserProperties(string strUserName) 
    { 
     UserPrincipal up = GetUser(strUserName); 
     if (up != null) 
     { 
      string firstName = up.GivenName; 
      string lastName = up.Surname; 
      string middleInit = String.IsNullOrEmpty(up.MiddleName) ? "" : up.MiddleName.Substring(0, 1); 
      string email = up.EmailAddress; 
      string location = String.Empty; 
      string phone = String.Empty; 
      string office = String.Empty; 
      string dept = String.Empty; 

      DirectoryEntry de = (DirectoryEntry)up.GetUnderlyingObject(); 
      DirectorySearcher ds = new DirectorySearcher(de); 
      ds.PropertiesToLoad.Add("l"); // city field, a.k.a location 
      ds.PropertiesToLoad.Add("telephonenumber"); 
      ds.PropertiesToLoad.Add("department"); 
      ds.PropertiesToLoad.Add("physicalDeliveryOfficeName"); 

      SearchResultCollection results = ds.FindAll(); 
      if (results != null && results.Count > 0) 
      { 
       ResultPropertyCollection rpc = results[0].Properties; 
       foreach (string rp in rpc.PropertyNames) 
       { 
        if (rp == "l") // this matches the "City" field in AD properties 
         location = rpc["l"][0].ToString(); 
        if (rp == "telephonenumber") 
         phone = FormatPhoneNumber(rpc["telephonenumber"][0].ToString());      
        if (rp == "physicalDeliveryOfficeName") 
         office = rpc["physicalDeliveryOfficeName"][0].ToString(); 
        if (rp == "department") 
         dept = rpc["department"][0].ToString(); 
       } 
      } 

      string[] userProps = new string[10]; 
      userProps[0] = strUserName; 
      userProps[1] = firstName; 
      userProps[2] = lastName; 
      userProps[3] = up.MiddleName; 
      userProps[4] = middleInit; 
      userProps[5] = email; 
      userProps[6] = location; 
      userProps[7] = phone; 
      userProps[8] = office; 
      userProps[9] = dept; 

      return userProps; 
     } 
     else 
      return null; 
    } 

    /// <summary> 
    /// Returns a UserPrincipal (AD) user object based on string userID being supplied 
    /// </summary> 
    /// <param name="strUserName">String form of User ID: domain\username</param> 
    /// <returns>UserPrincipal object</returns> 
    public static UserPrincipal GetUser(string strUserName) 
    { 
     PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain); 
     try 
     { 
      UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(oPrincipalContext, strUserName); 
      return oUserPrincipal; 
     } 
     catch (Exception ex) { return null; } 
    } 

    public static string FormatPhoneNumber(string strPhoneNumber) 
    { 
     if (strPhoneNumber.Length > 0) 
      // return String.Format("{0:###-###-####}", strPhoneNumber); // formating does not work because strPhoneNumber is a string and not a number 
      return Regex.Replace(strPhoneNumber, @"(\d{3})(\d{3})(\d{4})", "$1-$2-$3"); 
     else 
      return strPhoneNumber; 
    } 

注意FormatPhoneNumber函数用于北美数字。它会发现一个数字(##########)并将其分成###-###-####

你可以再拿到这类似的房屋,早在foreach循环:

string[] userProps = GetUserProperties(userId); 
string office = userProps[8]; 

但是,作为一个整体的解决方案,你甚至可以这些结果添加到DataRow列,并返回它作为部分DataTable,然后您可以绑定到ListViewGridView。这是我做到了,在充满我所需要的性能List<string>发送:

/// <summary> 
    /// Gets matches based on First and Last Names. 
    /// This function takes a list of acceptable properties: 
    /// USERNAME 
    /// MIDDLE_NAME 
    /// MIDDLE_INITIAL 
    /// EMAIL 
    /// LOCATION 
    /// POST 
    /// PHONE 
    /// OFFICE 
    /// DEPARTMENT 
    /// 
    /// The DataTable returned will have columns with these names, and firstName and lastName will be added to a column called "NAME" 
    /// as the first column, automatically. 
    /// </summary> 
    /// <param name="firstName"></param> 
    /// <param name="lastName"></param> 
    /// <param name="props"></param> 
    /// <returns>DataTable of columns from "props" based on first and last name results</returns> 
    public static DataTable GetUsersFromName(string firstName, string lastName, List<string> props) 
    { 
     string userId = String.Empty; 
     int resultCount = 0; 

     DataTable dt = new DataTable(); 
     DataRow dr; 
     DataColumn dc; 

     // Always set the first column to the Name we pass in 
     dc = new DataColumn(); 
     dc.DataType = System.Type.GetType("System.String"); 
     dc.ColumnName = "NAME"; 
     dt.Columns.Add(dc); 

     // Establish our property list as columns in our DataTable 
     if (props != null && props.Count > 0) 
     { 
      foreach (string s in props) 
      { 
       dc = new DataColumn(); 
       dc.DataType = System.Type.GetType("System.String"); 
       if (!String.IsNullOrEmpty(s)) 
       { 
        dc.ColumnName = s; 
        dt.Columns.Add(dc); 
       } 
      } 
     } 

     // Start our search 
     PrincipalContext ctx = new PrincipalContext(ContextType.Domain); 

     UserPrincipal up = new UserPrincipal(ctx); 
     if (!String.IsNullOrEmpty(firstName)) 
      up.GivenName = firstName; 
     if (!String.IsNullOrEmpty(lastName)) 
      up.Surname = lastName; 

     PrincipalSearcher srch = new PrincipalSearcher(up); 
     srch.QueryFilter = up; 

     using (PrincipalSearchResult<Principal> results = srch.FindAll()) 
     { 
      if (results != null) 
      { 
       resultCount = results.Count(); 
       if (resultCount > 0) // we have results 
       { 
        foreach (Principal found in results) 
        { 
         // Iterate results, set into DataRow, add to DataTable 
         dr = dt.NewRow(); 
         dr["NAME"] = found.DisplayName; 

         if (props != null && props.Count > 0) 
         { 
          userId = GetUserIdFromPrincipal(found); 

          // Get other properties 
          string[] userProps = GetUserProperties(userId); 

          foreach (string s in props) 
          { 
           if (s == "USERNAME")     
            dr["USERNAME"] = userId; 

           if (s == "MIDDLE_NAME") 
            dr["MIDDLE_NAME"] = userProps[3]; 

           if (s == "MIDDLE_INITIAL") 
            dr["MIDDLE_INITIAL"] = userProps[4]; 

           if (s == "EMAIL") 
            dr["EMAIL"] = userProps[5]; 

           if (s == "LOCATION") 
            dr["LOCATION"] = userProps[6]; 

           if (s == "PHONE") 
            dr["PHONE"] = userProps[7]; 

           if (s == "OFFICE") 
            dr["OFFICE"] = userProps[8];          

           if (s == "DEPARTMENT") 
            dr["DEPARTMENT"] = userProps[9]; 
          } 
         } 
         dt.Rows.Add(dr); 
        } 
       } 
      } 
     } 

     return dt; 
    } 

你会调用该函数是这样的:

string firstName = txtFirstName.Text; 
string lastName = txtLastName.Text; 

List<string> props = new List<string>(); 
props.Add("OFFICE"); 
props.Add("DEPARTMENT"); 
props.Add("LOCATION"); 
props.Add("USERNAME"); 

DataTable dt = GetUsersFromName(firstName, lastName, props); 

DataTable将充满这些列,并作为第一列的NAME列,其将具有来自AD的用户的实际.DisplayName

注:您必须引用System.DirectoryServicesSystem.DirectoryServices.AccountManagementSystem.Text.RegularExpressionsSystem.Data使用这一切。

HTH!

0

我正在寻找在这篇文章中的代码是:

 string uid = Properties.Settings.Default.uid; 
     string pwd = Properties.Settings.Default.pwd; 
     using (var context = new PrincipalContext(ContextType.Domain, "YOURDOMAIN", uid, pwd)) 
     { 
      using (UserPrincipal user = new UserPrincipal(context)) 
      { 
       user.GivenName = "*adolf*"; 
       using (var searcher = new PrincipalSearcher(user)) 
       { 
        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("Mail: " + de.Properties["mail"].Value); 

         PrincipalSearchResult<Principal> groups = result.GetGroups(); 

         foreach (Principal item in groups) 
         { 
          Console.WriteLine("Groups: {0}: {1}", item.DisplayName, item.Name); 
         } 
         Console.WriteLine(); 
        } 
       } 
      } 
     } 
     Console.WriteLine("End"); 
     Console.ReadLine(); 

似乎通配符任何字符是星号(*)。这就是为什么:

user.GivenName = "*firstname*"; 

详情请阅读Microsoft documentation

相关问题