Как вы можете найти пользователя в активной директории из С#?
Я пытаюсь выяснить, как искать AD с С# аналогично тому, как "Поиск пользователей, контактов и групп" работает в инструменте "Пользователи и компьютеры Active Directory". У меня есть строка, которая либо содержит имя группы, либо имя пользователя (обычно в формате firstname middleinitial [если у них есть] последнее имя, но не всегда). Даже если я делаю отдельный запрос для групп и пользователей, я не могу найти способ поиска, который захватывает большинство учетных записей пользователей. Инструмент "Найти пользователей, контакты и группы" возвращает их почти каждый раз. У кого-нибудь есть предложения?
Я уже знаю, как использовать класс DirectorySearcher, проблема в том, что я не могу найти запрос, который делает то, что мне хотелось бы. Ни имя cn, ни имя samaccount не имеют никакого отношения к имени пользователя в этом, поэтому я не могу выполнить поиск по ним. Разделение вещей и поиск по sn и givenName не улавливаются почти так же сильно, как этот инструмент.
Ответы
Ответ 1
Вы используете .NET 3.5? Если это так - AD имеет отличные новые возможности в .NET 3.5 - ознакомьтесь с этой статьей Руководители принципов безопасности в .NET 3.5 от Ethan Wilanski и Joe Kaplan.
Одной из больших новых функций является класс "PrincipalSearcher", который должен значительно упростить поиск пользователей и/или групп в AD.
Если вы не можете использовать .NET 3.5, то одна вещь, которая может облегчить вашу жизнь, называется "Неоднозначное разрешение имен", и это малоизвестный специальный фильтр поиска, который будет искать практически любой атрибут, связанный с именем.
Укажите свой поисковый запрос LDAP следующим образом:
searcher.Filter = string.Format("(&(objectCategory=person)(anr={0}))", yourSearchTerm)
Кроме того, я бы рекомендовал фильтровать атрибут "objectCategory", так как этот однозначный и индексированный по умолчанию в AD, что намного быстрее, чем использование "objectClass".
Марк
Ответ 2
System.DirectoryServices имеет два пространства имен: DirectoryEntry и DirectorySearcher.
Дополнительная информация о DirectorySearcher здесь:
http://msdn.microsoft.com/en-us/library/system.directoryservices.directorysearcher.aspx
Затем вы можете использовать свойство Filter для фильтрации по Group, пользователю и т.д.
Итак, если вы хотите фильтровать по имени учетной записи, вы должны установить для .Filter значение:
"(&(sAMAccountName=bsmith))"
и запустите метод FilterAll. Это вернет SearchResultCollection, в котором вы можете прокручивать и извлекать информацию о пользователе.
Ответ 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(или псевдоним) обычно является именем sAMAccountName FirstName LastName обычно будет CN
Ответ 4
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;
}
Ответ 5
Получил это от Джо Каплан и Этан Вилански Статья
Используйте это использование (из ссылки на dll System.DirectoryServices.AccountManagement):
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;
}
Ответ 6
Чтобы добавить ответ Мияги....
Здесь фильтр/запрос для применения к DirectorySearcher
DirectorySearcher ds = new DirectorySearcher();
ds.Filter = "samaccountname=" + userName;
SearchResult result = ds.FindOne();
Ответ 7
Другие ответы были плохо описаны, не описывали, как их реализовать, и большинство из них дали неправильные свойства фильтра. Вам даже не нужно использовать .Filter
- вы можете просто назначить свои свойства (фамилия = .Surname
, first name = .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;
Я предполагаю, что у вас есть текстовые поля для первого и последнего имени, чтобы получить его, с идентификаторами/именами txtFirstName
и txtLastName
. Обратите внимание: если вы не имеете значения в свойстве, которое вы ищете, не добавляйте его в UserPrincipal
, иначе это вызовет исключение. Это причина проверок, которые я включил, выше.
Затем вы выполните .FindAll
on 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
для получения необходимых вам свойств, и это отвечает на вопрос о том, как найти пользователя в AD с использованием С#, но обратите внимание, что вы можете получить только несколько свойств с помощью объекта Principal
и если бы я дошел до этого вопроса через Google (как и я), я был бы очень уныл. Если вы обнаружите, что все, что вам нужно - здорово, все готово! Но для того, чтобы получить остальное (и успокоить собственную совесть), вы должны опуститься, и я опишу, как это сделать.
Я обнаружил, что вы не можете просто использовать этот 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
будет заполнен этими столбцами и столбцом NAME
в качестве первого столбца, который будет иметь фактический пользователь .DisplayName
из AD.
Примечание. Для использования всего этого необходимо указать System.DirectoryServices
и System.DirectoryServices.AccountManagement
, System.Text.RegularExpressions
, System.Data
.
НТН!
Ответ 8
Код, который я искал в этом сообщении, был:
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*";
Подробнее в Документация по Microsoft