Active Directory에서 사용자 그룹을 가져 오는 방법은 무엇입니까? (c #, asp.net)


109

이 코드를 사용하여 현재 사용자의 그룹을 가져옵니다. 하지만 수동으로 사용자에게 부여한 다음 그룹을 가져오고 싶습니다. 어떻게 할 수 있습니까?

using System.Security.Principal;

public ArrayList Groups()
{
    ArrayList groups = new ArrayList();

    foreach (IdentityReference group in System.Web.HttpContext.Current.Request.LogonUserIdentity.Groups)
    {
        groups.Add(group.Translate(typeof(NTAccount)).ToString());
    }

    return groups;
}

답변:


163

.NET 3.5 이상을 사용하는 경우 새 System.DirectoryServices.AccountManagement(S.DS.AM) 네임 스페이스를 사용하면 이전보다 훨씬 쉽게 만들 수 있습니다.

여기에서 모든 내용을 읽어보십시오 . .NET Framework 3.5에서 디렉터리 보안 주체 관리

업데이트 : 이전 MSDN 잡지 기사가 더 이상 온라인 상태가 아닙니다. 안타깝게도 Microsoft에서 2008 년 1 월 MSDN 잡지 CHM다운로드하고 그 기사를 읽어야합니다.

기본적으로 "주요 컨텍스트"(일반적으로 도메인), 사용자 주체가 있어야하며 그룹을 매우 쉽게 얻을 수 있습니다.

public List<GroupPrincipal> GetGroups(string userName)
{
   List<GroupPrincipal> result = new List<GroupPrincipal>();

   // establish domain context
   PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain);

   // find your user
   UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, userName);

   // if found - grab its groups
   if(user != null)
   {
      PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups();

      // iterate over all groups
      foreach(Principal p in groups)
      {
         // make sure to add only group principals
         if(p is GroupPrincipal)
         {
             result.Add((GroupPrincipal)p);
         }
      }
   }

   return result;
}

그게 전부입니다! 이제 사용자가 속한 권한 그룹의 결과 (목록)가 생겼습니다.이를 반복하고 이름을 인쇄하거나 필요한 모든 작업을 수행합니다.

업데이트 :UserPrincipal 개체 에 표시되지 않는 특정 속성에 액세스 하려면 기본을 파헤쳐 야합니다 DirectoryEntry.

public string GetDepartment(Principal principal)
{
    string result = string.Empty;

    DirectoryEntry de = (principal.GetUnderlyingObject() as DirectoryEntry);

    if (de != null)
    {
       if (de.Properties.Contains("department"))
       {
          result = de.Properties["department"][0].ToString();
       }
    }

    return result;
}

업데이트 # 2 : 이 두 코드 조각을 합치는 것이 너무 어렵지 않을 것 같습니다 ....하지만 괜찮습니다.

public string GetDepartment(string username)
{
    string result = string.Empty;

    // if you do repeated domain access, you might want to do this *once* outside this method, 
    // and pass it in as a second parameter!
    PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain);

    // find the user
    UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, username);

    // if user is found
    if(user != null)
    {
       // get DirectoryEntry underlying it
       DirectoryEntry de = (user.GetUnderlyingObject() as DirectoryEntry);

       if (de != null)
       {
          if (de.Properties.Contains("department"))
          {
             result = de.Properties["department"][0].ToString();
          }
       }
    }

    return result;
}

@Tassisto : 안타깝게도 해당 속성은 직접 사용할 수 없습니다 UserPrincipal. 액세스 방법은 업데이트 된 답변을 참조하세요.
marc_s 2011 년

나는 그것의 데파트 망 필드의 값을 얻을 수있는 사용자 이름을 제공해야
Tassisto

@Tassito : 그럼 1) 도메인 컨텍스트를 만들고, 2) 이름으로 해당 사용자를 찾고, 3) 내 코드를 사용하여 부서를 가져옵니다
marc_s 2011 년

1
GetGroups 메서드가 작동하지 않았습니다. 다음과 같이 생성자의 다른 오버로드를 사용하도록 새 주체 컨텍스트를 변경했습니다. PrincipalContext yourDomain = new PrincipalContext (ContextType.Domain, "192.168.2.23", "domain \ user", "password" ); 항상 Active Directory 인증을 통해 로그인하는 것은 아니기 때문에 완전히 논리적입니다. 도움이
되기를 바랍니다

2
이 대답은 훌륭합니다. 다음과 같이 그룹 반복을 단순화 할 수도 있습니다. result.AddRange (user.GetAuthorizationGroups (). OfType <GroupPrincipal> ()
tlbignerd

59

GetAuthorizationGroups()중첩 된 그룹을 찾지 못합니다. 특정 사용자가 구성원 인 모든 그룹 (중첩 된 그룹 포함)을 실제로 얻으려면 다음을 시도하십시오.

using System.Security.Principal

private List<string> GetGroups(string userName)
{
    List<string> result = new List<string>();
    WindowsIdentity wi = new WindowsIdentity(userName);

    foreach (IdentityReference group in wi.Groups)
    {
        try
        {
            result.Add(group.Translate(typeof(NTAccount)).ToString());
        }
        catch (Exception ex) { }
    }
    result.Sort();
    return result;
}

내가 사용하는 try/catch일부 SID를 더 이상 사용할 수 있기 때문에 매우 큰 AD 200 개 그룹이 밖으로 몇 가지 예외가 없었기 때문에. ( Translate()호출은 SID-> 이름 변환을 수행합니다.)


3
AD를 통해 실행하는 대신이 기술을 사용하여 성능이 향상되었습니다. 감사합니다!
Philippe

GetAuthorisationGroups ()는 저에게 매우 느립니다. 즉 26 개이며 지금까지 찾은 다른 모든 코드에는 Everyone, Domain Users 등과 같은 잘 알려진 식별자가 포함되어 있지 않습니다. 제공 한 코드는 문자 그대로 즉각적이며 모든 sid를 포함합니다. 네 sids 만 있지만 잘 알려진 사용자 정의를 포함하여 내가 필요로하는 것입니다!
Thierry

19

우선 GetAuthorizationGroups ()는 훌륭한 함수이지만 불행히도 두 가지 단점이 있습니다.

  1. 특히 사용자와 그룹이 많은 대기업에서는 성능이 좋지 않습니다. 실제로 필요한 것보다 훨씬 많은 데이터를 가져오고 결과의 각 루프 반복에 대해 서버 호출을 수행합니다.
  2. 여기에는 그룹과 사용자가 진화 할 때 애플리케이션이 '언젠가'작동을 멈출 수있는 버그가 포함되어 있습니다. Microsoft는 문제를 인식했으며 일부 SID와 관련이 있습니다. 표시되는 오류는 "그룹을 열거하는 동안 오류가 발생했습니다."입니다.

따라서 GetAuthorizationGroups ()를 더 나은 성능과 오류 안전으로 대체하는 작은 함수를 작성했습니다. 인덱싱 된 필드를 사용하는 쿼리로 LDAP 호출을 1 회만 수행합니다. 그룹 이름 ( "cn"속성)보다 더 많은 속성이 필요한 경우 쉽게 확장 할 수 있습니다.

// Usage: GetAdGroupsForUser2("domain\user") or GetAdGroupsForUser2("user","domain")
public static List<string> GetAdGroupsForUser2(string userName, string domainName = null)
{
    var result = new List<string>();

    if (userName.Contains('\\') || userName.Contains('/'))
    {
        domainName = userName.Split(new char[] { '\\', '/' })[0];
        userName = userName.Split(new char[] { '\\', '/' })[1];
    }

    using (PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, domainName))
        using (UserPrincipal user = UserPrincipal.FindByIdentity(domainContext, userName))
            using (var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domainContext.Name)))
            {
                searcher.Filter = String.Format("(&(objectCategory=group)(member={0}))", user.DistinguishedName);
                searcher.SearchScope = SearchScope.Subtree;
                searcher.PropertiesToLoad.Add("cn");

                foreach (SearchResult entry in searcher.FindAll())
                    if (entry.Properties.Contains("cn"))
                        result.Add(entry.Properties["cn"][0].ToString());
            }

    return result;
}

대박! 고맙습니다. 일부 코드를 작성하기 시작했고 GetAuthorizationGroups를 사용하고 있었는데 모든 그룹을 가져 오는 데 300ms-2.5s가 걸린다는 사실에 놀랐습니다. 귀하의 방법은 20-30ms 내에 완료됩니다.
Keith

4
이것은 유망 해 보였지만 중첩 된 그룹을 해결하지 않습니다. 예를 들어 사용자가 그룹 x의 구성원 인 그룹 a의 구성원입니다. 위의 코드는 그룹 a 만 표시하고 그룹 x는 표시하지 않습니다. : 나는 tokenGroups를 통해이 방법을 사용 stackoverflow.com/a/4460658/602449
로버트 Muehsig

Robert Muehsig의 의견을 살펴보십시오. 이것은 중첩 된 그룹을 수행하며 훨씬 더 빠릅니다. 유일한 단점은 배포 그룹이 아닌 보안 그룹 만 반환합니다
Nick Rubino

@bigjim은 데이터를 반환하는 데 거의 6 초가 걸리기 때문에 GetAuthorizationGroups를 사용할 수 없지만 제공 한 코드는 Everyone, Domain Users 등과 같은 잘 알려진 그룹을 반환하지 않습니다. 모든 것은 사용자가 속한 모든 그룹이 아닌 "사용자 지정 그룹"만 반환하는 것처럼 보입니다.
Thierry

11

AD 내에서 모든 사용자는 속성을 가지고 있습니다 memberOf. 여기에는 그가 속한 모든 그룹의 목록이 포함됩니다.

다음은 간단한 코드 예입니다.

// (replace "part_of_user_name" with some partial user name existing in your AD)
var userNameContains = "part_of_user_name";

var identity = WindowsIdentity.GetCurrent().User;
var allDomains = Forest.GetCurrentForest().Domains.Cast<Domain>();

var allSearcher = allDomains.Select(domain =>
{
    var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domain.Name));

    // Apply some filter to focus on only some specfic objects
    searcher.Filter = String.Format("(&(&(objectCategory=person)(objectClass=user)(name=*{0}*)))", userNameContains);
    return searcher;
});

var directoryEntriesFound = allSearcher
    .SelectMany(searcher => searcher.FindAll()
        .Cast<SearchResult>()
        .Select(result => result.GetDirectoryEntry()));

var memberOf = directoryEntriesFound.Select(entry =>
{
    using (entry)
    {
        return new
        {
            Name = entry.Name,
            GroupName = ((object[])entry.Properties["MemberOf"].Value).Select(obj => obj.ToString())
        };
    }
});

foreach (var item in memberOf)
{
    Debug.Print("Name = " + item.Name);
    Debug.Print("Member of:");

    foreach (var groupName in item.GroupName)
    {
        Debug.Print("   " + groupName);
    }

    Debug.Print(String.Empty);
}
}

1
@Tassisto : 네, 그는 당신을 이해합니다. 위의 코드 스 니펫은 원하는대로 정확하게 수행됩니다. 최종 foreach 루프를 디버그 인쇄 대신 그룹 이름 목록을 생성하는 루프로 바꾸면됩니다.
Joel Etherton 2011 년

2
사용자의 기본 그룹 (종종 도메인 사용자)을 나열하지 못합니다. 돌아가서 해당 정보를 별도로 쿼리해야합니다. GetAuthorizationGroups에는이 문제가 없습니다.
Andy

1

제 경우에는 아무런 설명없이 GetGroups ()를 계속 사용할 수있는 유일한 방법은 AD (Active Directory)를 읽을 수있는 권한이있는 그룹에 사용자 (USER_WITH_PERMISSION)를 추가하는 것입니다. 이 사용자와 암호를 전달하는 PrincipalContext를 구성하는 것은 매우 중요합니다.

var pc = new PrincipalContext(ContextType.Domain, domain, "USER_WITH_PERMISSION", "PASS");
var user = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, userName);
var groups = user.GetGroups();

Active Directory 내부에서 수행 할 수있는 단계는 다음과 같습니다.

  1. Active Directory에 그룹을 생성 (또는 가져 오기)하고 보안 탭에서 "Windows 인증 액세스 그룹"을 추가합니다.
  2. "고급"버튼을 클릭하십시오
  3. "Windows 인증 액세스 그룹"을 선택하고 "보기"를 클릭하십시오.
  4. "읽기 tokenGroupsGlobalAndUniversal"확인
  5. 원하는 사용자를 찾아 첫 번째 단계에서 생성 (취득) 한 그룹에 추가

1
웹 애플리케이션에서 서비스 / 앱 풀 계정에 기본 제공 계정을 사용하는 경우이 문제가 발생할 수 있습니다. 도메인 계정을 서비스 / 앱 풀 계정으로 사용하거나 코드 내에서 도메인 계정을 가장하는 경우 기본적으로 읽기 권한이 있어야하며이 문제가 없어야합니다.
vapcguy

1

이것은 나를 위해 작동합니다

public string[] GetGroupNames(string domainName, string userName)
    {
        List<string> result = new List<string>();

        using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, domainName))
        {
            using (PrincipalSearchResult<Principal> src = UserPrincipal.FindByIdentity(principalContext, userName).GetGroups())
            {
                src.ToList().ForEach(sr => result.Add(sr.SamAccountName));
            }
        }

        return result.ToArray();
    }

1

대답은 검색하려는 그룹의 종류에 따라 다릅니다. System.DirectoryServices.AccountManagement네임 스페이스는 두 그룹 검색 방법을 제공한다 :

GetGroups- 현재 보안 주체가 구성원 인 그룹을 지정하는 그룹 개체 컬렉션을 반환합니다.

이 오버로드 된 메서드는 보안 주체가 직접 구성원 인 그룹 만 반환합니다. 재귀 검색이 수행되지 않습니다.

GetAuthorizationGroups- 이 사용자가 구성원 인 모든 권한 부여 그룹을 포함하는 주체 개체의 컬렉션을 반환합니다. 이 함수는 보안 그룹 인 그룹 만 반환합니다. 메일 그룹은 반환되지 않습니다.

이 메서드는 모든 그룹을 재귀 적으로 검색하고 사용자가 구성원 인 그룹을 반환합니다. 반환 된 집합에는 시스템이 권한 부여 목적으로 사용자를 구성원으로 간주하는 추가 그룹이 포함될 수도 있습니다.

그래서 GetGroups얻는 모든 사용자가 어떤 그룹 의 직접 멤버, 그리고 GetAuthorizationGroups모든 가져 인증 사용자가 어떤 그룹에 직접 또는 간접 멤버.

이름이 지정되는 방식에도 불구하고 하나는 다른 하나의 하위 집합이 아닙니다. 에 의해 반환 GetGroups되지 않은 그룹이있을 수 있으며 GetAuthorizationGroups그 반대의 경우도 마찬가지입니다.

다음은 사용 예입니다.

PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, "MyDomain", "OU=AllUsers,DC=MyDomain,DC=Local");
UserPrincipal inputUser = new UserPrincipal(domainContext);
inputUser.SamAccountName = "bsmith";
PrincipalSearcher adSearcher = new PrincipalSearcher(inputUser);
inputUser = (UserPrincipal)adSearcher.FindAll().ElementAt(0);
var userGroups = inputUser.GetGroups();

1

내 솔루션 :

UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain, myDomain), IdentityType.SamAccountName, myUser);
List<string> UserADGroups = new List<string>();            
foreach (GroupPrincipal group in user.GetGroups())
{
    UserADGroups.Add(group.ToString());
}

0

Translate가 로컬에서 작동하지만 원격 ei 그룹이 아닌 경우. 번역하기 (typeof (NTAccount)

LOGGED IN USER ID를 사용하여 애플리케이션 코드를 실행하려면 가장을 활성화하십시오. IIS를 통해 또는 web.config에 다음 요소를 추가하여 가장을 활성화 할 수 있습니다 .

<system.web>
<identity impersonate="true"/>

가장이 활성화 된 경우 응용 프로그램은 사용자 계정에있는 권한을 사용하여 실행됩니다. 따라서 로그인 한 사용자가 특정 네트워크 리소스에 액세스 할 수있는 경우에만 응용 프로그램을 통해 해당 리소스에 액세스 할 수 있습니다.

그의 성실한 비디오에서 얻은 정보에 대해 PRAGIM 기술에게 감사드립니다.

asp.net 파트 87의 Windows 인증 :

https://www.youtube.com/watch?v=zftmaZ3ySMc

그러나 가장은 서버에 많은 오버 헤드를 생성합니다.

특정 네트워크 그룹의 사용자를 허용하는 가장 좋은 솔루션 은 웹 구성에서 익명을 거부하는 것입니다. <authorization><deny users="?"/><authentication mode="Windows"/>

가급적 global.asax에서 코드 뒤에 HttpContext.Current.User.IsInRole을 사용하십시오 .

Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
If HttpContext.Current.User.IsInRole("TheDomain\TheGroup") Then
//code to do when user is in group
End If

참고 : 그룹은 백 슬래시 (예 : "TheDomain \ TheGroup")로 작성해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.