如何从C#中找到活动目录中的用户?

我试图弄清楚如何从C#中搜索AD,类似于“查找用户,联系人和组”在“Active Directory用户和计算机”工具中的工作方式。 我有一个字符串,其中包含一个组名或一个用户名(通常格式为firstname middleinitial [如果他们有一个]姓氏,但并非总是如此)。 即使我对群组与用户进行单独查询,我也无法想出一种捕获大多数用户帐户的搜索方式。 查找用户,联系人和组工具几乎每次都会将它们带回来。 有人有什么建议吗?

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

你是.NET 3.5吗? 如果是这样 – AD在.NET 3.5中有很多新function – 请参阅本文由Ethan Wilanski和Joe Kaplan 管理.NET 3.5中的目录安全主体 。

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

如果你不能使用.NET 3.5,那么可能会让你的生活更轻松的一件事叫做“模糊名称解析”,这是一个鲜为人知的特殊搜索filter,可以同时搜索任何与名称相关的属性。

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

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

另外,我建议对“objectCategory”属性进行过滤,因为这是AD中的单值和默认索引,这比使用“objectClass”快得多。

System.DirectoryServices有两个名称空间… DirectoryEntry和DirectorySearcher。

有关DirectorySearcher的更多信息:

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

然后,您可以使用Filter属性按组,用户等进行筛选…

因此,如果您想按帐户名称进行过滤,则可以将.Filter设置为:

 "(&(sAMAccountName=bsmith))" 

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

您需要根据您查找用户的方式构建搜索字符串。

 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
FirstName LastName通常是CN

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

从Joe Kaplan和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 results = pS.FindAll(); if (results != null && results.Count() > 0) return true; return false; } 

添加到Miyagi的答案….

这是一个应用于DirectorySearcher的filter/查询

 DirectorySearcher ds = new DirectorySearcher(); ds.Filter = "samaccountname=" + userName; SearchResult result = ds.FindOne(); 

其他答案描述不清楚,没有描述如何实现它们,并且大多数都给出了错误的filter属性。 您甚至不需要使用.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; 

我假设你有第一个和最后一个名字的文本框来获取它,ID /名称为txtFirstNametxtLastName 。 请注意,如果您在要查找的属性中没有值,请不要将其添加到UserPrincipal ,否则将导致exception。 这就是我在上面进行检查的原因。

然后,您在srch上执行.FindAll ,以将搜索结果转换为Principal对象的PrincipalSearchResult集合:

 using (PrincipalSearchResult 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 ,结果也不会为null,并且为什么两个检查都存在。

您使用该foreach迭代以获取所需的属性,这回答了如何使用C#在AD中查找用户的问题,但请注意,您只能使用Principal对象获取一些属性,如果我通过谷歌(就像我一样),我会非常沮丧。 如果您发现这就是您所需要的一切 – 那就太棒了! 但是为了得到rest(并保持自己的良心),你必须潜入,我将描述如何做到这一点。

我发现你不能只使用我上面提到的那个username ,但是你必须得到整个DOMAIN\doej这样的名字。 这就是你如何做到的。 相反,把它放在foreach循环中,上面:

 string userId = GetUserIdFromPrincipal(found); 

并使用此function:

 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, aka 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; } ///  /// Returns a UserPrincipal (AD) user object based on string userID being supplied ///  /// String form of User ID: domain\username /// UserPrincipal object 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里面填充了我需要的属性:

  ///  /// 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. ///  ///  ///  ///  /// DataTable of columns from "props" based on first and last name results public static DataTable GetUsersFromName(string firstName, string lastName, List 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 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 props = new List(); 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!

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

  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 groups = result.GetGroups(); foreach (Principal item in groups) { Console.WriteLine("Groups: {0}: {1}", item.DisplayName, item.Name); } Console.WriteLine(); } } } } Console.WriteLine("End"); Console.ReadLine(); 

似乎任何字符的通配符都是Asterisk(*)。 这就是为什么:

 user.GivenName = "*firstname*"; 

阅读Microsoft文档中的更多内容