2009-05-05 5 views
18

저는 Active Directory 사용자 및 컴퓨터 도구에서 "사용자, 연락처 및 그룹 찾기"가 작동하는 것과 비슷하게 C#에서 AD를 검색하는 방법을 알아 내려고하고 있습니다. 그룹 이름이나 사용자 이름이 포함 된 문자열이 있습니다. 일반적으로 firstname middleinitial (성이있는 경우 lastname이지만 항상 그런 것은 아닙니다) 형식입니다. 그룹 대 사용자에 대해 별도의 쿼리를 수행하더라도 대부분의 사용자 계정을 검색하는 검색 방법을 찾을 수 없습니다. 사용자, 연락처 및 그룹 찾기 도구는 거의 항상 그 도구를 다시 가져옵니다. 누구든지 어떤 제안이 있습니까?C#에서 활성 디렉토리의 사용자를 어떻게 찾을 수 있습니까?

나는 DirectorySearcher 클래스를 사용하는 방법을 이미 알고 있지만 문제는 내가 원했던 쿼리를 찾을 수 없다는 것입니다. cn 및 samaccount 이름은이 이름에서 사용자 이름과 관련이 없으므로 해당 이름을 검색 할 수 없습니다. 물건을 나눠서 sn과 givenName을 검색해도 그 도구만큼이나 가까운 곳에서는 잡을 수 없습니다.

+0

답변을 읽고있는 모든 SAMAccountName을 사용, 그들이 그렇게 많은 upvotes을 왜 모르겠어요 약 : 그 이유. 이 질문은 성과 이름을 사용하여 속성을 얻는 것에 관한 것입니다! 상단/표시된 정답만이 닫힙니다. – vapcguy

답변

18

이 .NET 3에 당신을 있습니까 DirectorySearcher에 적용 .5? 그렇다면 - AD에는 .NET 3.5의 새로운 기능이 추가되었습니다 - 이단 윌란스키와 조 카플란이 작성한 Managing Directory Security Principals in .NET 3.5을 확인하십시오.

큰 새로운 기능 중 하나는 광고에서 사용자 및/또는 그룹을 쉽게 찾을 수있는 "PrincipalSearcher"클래스입니다.

.NET 3.5를 사용할 수없는 경우 "모호한 이름 확인"이라고하는 것이 쉽지 않으며 한 번에 모든 이름 관련 속성을 한꺼번에 검색하는 약간 알려진 특수 검색 필터입니다 .

지정이 같이 당신의 LDAP 검색 쿼리 : 그 많은보다 더 빨리, 이는 단일 값 및 AD에서 기본적으로 인덱스 이후 또한

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

, 나는은 "수 objectcategory"속성에 필터링 추천 "objectClass"를 사용하여.

마크

+0

그게 내가 찾고 있던 바로 그거야! 고마워요! – Sunookitsune

10

System.DirectoryServices에는 두 개의 네임 스페이스가 있습니다. DirectoryEntry 및 DirectorySearcher. 당신이 계정 이름을 기준으로 필터링하고 싶다면

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

당신은 다음 등 그룹, 사용자에 의해 필터링 필터 속성을 사용할 수 있습니다 ... 그래서

: 여기 DirectorySearcher에

더 많은 정보 .Filter를 다음과 같이 설정합니다 :

"(&(sAMAccountName=bsmith))" 

그리고 FilterAll 메소드를 실행하십시오. 이렇게하면 사용자에 대한 정보를 반복하여 가져올 수있는 SearchResultCollection이 반환됩니다.

+0

DirectorySearcher에 대해 알고 있지만 고마워요. 문제는 광고에서 사용자를 찾기 위해 쿼리를 만들 수 없다는 것입니다. – Sunookitsune

+0

@Sunookitsune - 그래서 "사용자, 연락처 및 그룹 찾기"기능을 복제하려고합니다. –

+0

@ Miyagi Coder 필자는 그 기능을 정확히 말하지는 않지만 적어도 사용자를 검색하는 방법에 대해서는 무언가를 얻습니다. 또는 내가 다른 사람을 만났기 때문에 뭔가 다른 것을 찾으십시오. – Sunookitsune

4

사용자를 찾는 방법에 따라 검색 문자열을 만들어야합니다.

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을 할 것이다
이름 성 보통 여기

는 필터입니다 .... CN 미야기 현의 대답에 추가하려면

+0

아니요, 'sAMAccountName'은 사용자 이름입니다 (예 : Doej). Do는 사용자 이름이 아닙니다. 'userType'에'CN'을 사용하고'username' 대신에'FirstName LastName'을 넣으라고한다면, 실제로는 필터를 사용하는 것이 좋습니다. (& (objectCategory = user) (objectClass = user) (givenName = "+ firstName +") (sn = "+ lastName +")) ";"대신에. – vapcguy

2

될 것입니다/쿼리는

DirectorySearcher ds = new DirectorySearcher(); 

ds.Filter = "samaccountname=" + userName; 

SearchResult result = ds.FindOne(); 
+0

Bonzai Curtis !!!! –

+0

OP에는 사용자 이름, 즉 John Doe에 대한 DOMAIN \ doej가 없었습니다. 대신 그는 이름과 성을 받았습니다. 이 필터는 쓸모가 없습니다. – vapcguy

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; 
} 
3

합니다 (System.DirectoryServices.AccountManagement DLL을 참조에서)이 사용 Joe Kaplan and Ethan Wilansky 제 사용에서이있어 : 다른 답변을 잘못 설명했다

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; 
} 
+0

Downvote. OP는 'user.Name'과 같이 사용자 이름이 아닌 실제 이름을 가지고 있다고 말했다. 이렇게하면 이름, 성이 아닌 사용자 이름을 검색합니다. – vapcguy

0

을, 방법에 대해서는 설명하지 않았다 그들을 구현하고, 대부분은 잘못된 필터 속성을 부여했습니다. .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; 

당신은 txtFirstNametxtLastName의 ID/이름으로, 그걸 얻기 위해 첫 번째와 마지막 이름에 대한 텍스트 상자가 있습니다. 찾고있는 속성에 값이없는 경우 UserPrincipal에 값을 추가하지 않으면 예외가 발생합니다. 그것이 위의 수표에 대한 이유입니다. 그 .Count() 모두 검사가 왜 0, 그리고 경우에도 결과가 null 수 없음을

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. 
      } 
     } 
    } 
} 

참고 :

당신은 다음 PrincipalSearchResultPrincipal 오브젝트의 콜렉션으로 검색 결과를 얻을 수 srch.FindAll을 그곳에.

foreach을 사용하여 반복적으로 필요한 속성을 얻으십시오. 그러면 C#을 사용하여 AD에서 사용자를 찾는 방법에 대한 질문에 대답하지만 Principal 개체를 사용하는 몇 가지 속성 만 사용할 수 있습니다. Google을 통해이 질문에 도달했을 때 (나는 그랬던 것처럼) 매우 낙담 할 것입니다. 그게 당신이 필요로하는 모든 것을 발견했다면 - 끝났습니다! 그러나 나머지를 얻고 (그리고 내 자신의 양심을 쉬기 위해서), 당신은 잠수해야하며, 나는 그것을 어떻게하는지 설명 할 것입니다.

위의 내용을 username으로 바꿀 수는 없지만 전체 이름은 DOMAIN\doej이어야합니다. 이것이 당신이하는 방법입니다. 이 기능을 사용하면 것을 일단

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; 
} 

, 당신은 호출 할 수 있습니다 : 대신, 위, 그 foreach 루프에 넣고 :

string userId = GetUserIdFromPrincipal(found); 

이 기능을 사용

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 기능은 북미 번호 용입니다. 발견 된 번호 (##########)를 받아이를 ###-###-####으로 분리하십시오.

string[] userProps = GetUserProperties(userId); 
string office = userProps[8]; 

을하지만, 전체 솔루션으로, 당신은 DataRow 열에 결과를 추가도 할 수 있고,로 반환 :

그런 다음 해당 foreach 루프 다시,이 같은 특성을 얻을 수 있습니다 DataTable의 일부로 다음 ListView 또는 GridView에 바인딩 할 수 있습니다.

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는, 그 열이 작성되며 :이 같은이 함수를 호출 할

/// <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; 
    } 

이 내가 필요로하는 특성을 가득 List<string>에 전송, 그것을 어떻게입니다 첫 번째 열로 NAME 열을 입력하면 AD에서 사용자의 실제 .DisplayName이됩니다.

참고 : 당신은 System.DirectoryServicesSystem.DirectoryServices.AccountManagement, System.Text.RegularExpressions, System.Data이 모든 사용 참조해야합니다.

HTH!

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(); 

모든 문자에 대한 그 와일드 카드 별표 (*) 것 같다.

user.GivenName = "*firstname*"; 

아래 더에서 Microsoft documentation

관련 문제