Как вы можете найти пользователя в активном каталоге из C #? - PullRequest
18 голосов
/ 05 мая 2009

Я пытаюсь выяснить, как искать AD из C # аналогично тому, как «Поиск пользователей, контактов и групп» работает в инструменте Active Directory - пользователи и компьютеры. У меня есть строка, которая содержит либо имя группы, либо имя пользователя (обычно в формате firstname middleinitial [если у них есть] фамилия, но не всегда). Даже если я делаю отдельный запрос для групп и пользователей, я не могу найти способ поиска, который захватывает большинство учетных записей пользователей. Инструмент «Найти пользователей, контакты и группы» возвращает их почти каждый раз. У кого-нибудь есть предложения?

Я уже знаю, как использовать класс DirectorySearcher, проблема в том, что я не могу найти запрос, который делает то, что я хотел бы. Ни cn, ни samaccount name не имеют никакого отношения к имени пользователя в этом, поэтому я не могу искать по ним. Разбиение вещей и поиск по sn и GivenName не так близко, как у этого инструмента.

Ответы [ 8 ]

20 голосов
/ 05 мая 2009

Вы на .NET 3.5? Если это так - AD имеет замечательные новые функции в .NET 3.5 - ознакомьтесь с этой статьей Управление принципами безопасности каталогов в .NET 3.5 от Этана Вилански и Джо Каплана.

Одной из больших новых функций является класс "PrincipalSearcher", который должен значительно упростить поиск пользователей и / или групп в AD.

Если вы не можете использовать .NET 3.5, одна вещь, которая может облегчить вашу жизнь, называется «Разрешение неоднозначных имен», и это малоизвестный специальный поисковый фильтр, который будет искать практически любой атрибут, связанный с именем, одновременно.

Укажите свой поисковый запрос LDAP следующим образом:

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

Кроме того, я бы порекомендовал выполнить фильтрацию по атрибуту «objectCategory», поскольку он по умолчанию однозначный и индексируется в AD, что намного быстрее, чем при использовании «objectClass».

Марк

10 голосов
/ 05 мая 2009

System.DirectoryServices имеет два пространства имен ... DirectoryEntry и DirectorySearcher.

Более подробная информация о поиске каталогов здесь:

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

Затем можно использовать свойство Filter для фильтрации по группам, пользователям и т. Д. *

Так что, если вы хотите фильтровать по имени учетной записи, вы должны установить .Filter на:

"(&(sAMAccountName=bsmith))"

и запустите метод FilterAll. Это вернет коллекцию SearchResultCollection, которую вы можете просмотреть и получить информацию о пользователе.

3 голосов
/ 05 мая 2009
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 голосов
/ 05 мая 2009

Вам нужно построить строку поиска на основе того, как вы ищете пользователя.

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

2 голосов
/ 21 октября 2009

Получил это от Джо Каплана и Итана Вилански Статья Используйте это Используя (из ссылки на 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;
}
1 голос
/ 03 ноября 2016

Другие ответы были плохо описаны, не описывали, как их реализовать, и большинство из них дали неправильные свойства фильтра. Вам даже не нужно использовать .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;

Я предполагаю, что у вас есть текстовые поля для имени и фамилии, чтобы получить его, с идентификаторами / именами 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, чтобы получить нужные вам свойства, и это отвечает на вопрос о том, как найти пользователя в AD с помощью C #, но учтите, что вы можете получить только несколько свойств, используя объект 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, чтобы использовать все это.

НТН!

1 голос
/ 05 мая 2009

Чтобы добавить ответ Мияги ....

Вот фильтр / запрос для применения к DirectorySearcher

DirectorySearcher ds = new DirectorySearcher();

ds.Filter = "samaccountname=" + userName;

SearchResult result = ds.FindOne();
0 голосов
/ 23 ноября 2016

Код, который я искал в этом посте:

        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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...