Подтвердить что ты не робот

Как вы можете найти пользователя в активной директории из С#?

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

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

4b9b3361

Ответ 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