我正在尝试弄清楚如何从C#中搜索AD,类似于“查找用户,联系人和组”在“Active Directory用户和计算机”工具中的工作方式。我有一个字符串,其中包含一个组名或一个用户名(通常格式为firstname middleinitial [如果他们有一个]姓氏,但并非总是如此)。即使我对群组与用户进行单独查询,我也无法想出一种能够捕获大多数用户帐户的搜索方式。 “查找用户”,“联系人”和“组”工具几乎每次都会将它们带回。有人有什么建议吗?
我已经知道如何使用DirectorySearcher类,问题是我找不到我想做的查询。 cn和samaccount名称都没有与用户的名字有关,所以我无法搜索这些名称。拆分和搜索sn和givenName并没有像该工具那样接近任何地方。
答案 0 :(得分:20)
您使用的是.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”属性进行过滤,因为这是AD中的单值和默认索引,这比使用“objectClass”快得多。
马克
答案 1 :(得分:10)
System.DirectoryServices有两个名称空间...... DirectoryEntry和DirectorySearcher。
此处有关DirectorySearcher的更多信息:
http://msdn.microsoft.com/en-us/library/system.directoryservices.directorysearcher.aspx
然后,您可以使用Filter属性按组,用户等进行过滤...
因此,如果您想按帐户名称进行过滤,请将.Filter设置为:
"(&(sAMAccountName=bsmith))"
并运行FilterAll方法。这将返回一个SearchResultCollection,您可以循环并提取有关该用户的信息。
答案 2 :(得分:3)
您需要根据您寻找用户的方式构建搜索字符串。
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
答案 3 :(得分: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;
}
答案 4 :(得分:2)
来自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;
}
答案 5 :(得分:1)
添加Miyagi的回答......
这是一个应用于DirectorySearcher的过滤器/查询
DirectorySearcher ds = new DirectorySearcher();
ds.Filter = "samaccountname=" + userName;
SearchResult result = ds.FindOne();
答案 6 :(得分:1)
其他答案描述不清楚,没有描述如何实现它们,并且大多数都给出了错误的过滤器属性。您甚至不需要使用.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 /名称为txtFirstName
和txtLastName
。请注意,如果您要查找的属性中没有值,请不要将其添加到UserPrincipal
,否则会导致异常。这就是我在上面进行检查的原因。
然后在.FindAll
上执行srch
,将搜索结果转换为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中查找用户的问题,但请注意,您只能使用{{1}获取一些属性对象,如果我通过Google(正如我所做的那样)达到这个问题,我会非常沮丧。如果您发现这就是您所需要的一切 - 那就太棒了!但是为了得到其余的(并保持自己的良心),你必须潜入,我将描述如何做到这一点。
我发现你不能只使用我在上面提到的那个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
或ListView
。我就这样做了,发送了一个填充了我需要的属性的GridView
:
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
列将作为第一列,其中包含用户来自AD的实际NAME
。
注意:您必须引用.DisplayName
和System.DirectoryServices
,System.DirectoryServices.AccountManagement
,System.Text.RegularExpressions
才能使用这一切。
HTH!
答案 7 :(得分: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();
似乎任何字符的通配符都是Asterisk(*)。这就是原因:
user.GivenName = "*firstname*";
中了解详情