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マガジンの古い記事はもうオンラインではありません。2008年1月のMSDNマガジンのCHMを Microsoft からダウンロードし、そこで記事を読む必要があります。

基本的に、「プリンシパルコンテキスト」(通常はドメイン)、ユーザープリンシパルが必要であり、そのグループを非常に簡単に取得できます。

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:これらの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年

departement-fieldの値を取得するためにユーザー名を指定する必要があります
Tassisto

@Tassito:それでは、1)ドメインコンテキストを作成し、2)名前でそのユーザーを見つけ、3)コードスニペットを使用して部門を取得します
marc_s

1
GetGroupsメソッドが機能しませんでした。次のように、コンストラクターの別のオーバーロードを使用するように新しいプリンシパルコンテキストを変更しました:PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain、 "192.168.2.23"、 "domain \ user"、 "password" ); Active Directory認証を介して常にログインしているわけではないため、完全に論理的です。それがお役に立てば幸い
Omid S. 2014

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個のグループの2アウトしていくつかの例外がありませんでしたので。(Translate()呼び出しはSID->名前変換を行います。)


3
ADで実行する代わりにこの手法を使用することにより、パフォーマンスが向上しました。ありがとうございました!
フィリップ

GetAuthorisationGroups()は私にとって非常に遅いです。つまり26と私がこれまでに見つけた他のすべてのコードには、Everyone、Domain Usersなどのよく知られた識別子が含まれていませんでした。はい、sidのみですが、よく知られているカスタムのものを含め、それが必要です!
ティエリー

19

まず、GetAuthorizationGroups()は優れた関数ですが、残念ながら2つの欠点があります。

  1. 特にユーザーやグループが多い大企業では、パフォーマンスが低下します。それはあなたが実際に必要とするより多くのデータをフェッチし、結果の各ループ反復に対してサーバー呼び出しを行います
  2. これには、グループやユーザーが進化しているときにアプリケーションが「いつか」機能しなくなる可能性のあるバグが含まれています。マイクロソフトはこの問題を認識しており、一部の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を使用していて、すべてのグループを取得するのに300ミリ秒から2.5秒かかることに恐怖を感じました。メソッドは20〜30ミリ秒で完了します。
キース

4
これは有望であるように見えましたが、ネストされたグループを解決しません。たとえば、ユーザーはグループxのメンバーであるグループaのメンバーです。上記のコードはグループaのみを表示し、グループxは表示しません。tokenGroupsを介してこのメ​​ソッドを使用しました:stackoverflow.com/a/4460658/602449
Robert Muehsig

Robert Muehsigのコメントを見てください-これはネストされたグループを実行し、さらに高速です。唯一の欠点は、配布グループではなくセキュリティグループのみが返される
Nick Rubino

@bigjimデータを返すのに6秒近くかかるのでGetAuthorizationGroupsを使用できませんが、指定したコードではEveryone、Domain Usersなどの既知のグループが返されないため、これらを用意する必要があります。そこにあるものはすべて「カスタムグループ」だけを返し、ユーザーが属するすべてのグループではないようです。
ティエリー

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
@タシスト:はい、彼はあなたを理解しています。上記のコードスニペットは、希望どおりに動作します。最後のforeachループを、デバッグ出力ではなく、グループ名のリストを生成するループに置き換えるだけです。
Joel Etherton、2011年

2
ユーザーのプライマリグループ(多くの場合、ドメインユーザー)の一覧表示は失敗します。戻って、その情報を個別に照会する必要があります。GetAuthorizationGroupsにはこの問題はありません。
アンディ

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 Authorization Access Group」を追加します
  2. 「詳細」ボタンをクリックします
  3. 「Windows Authorization Access Group」を選択し、「View」をクリックします
  4. 「tokenGroupsGlobalAndUniversalの読み取り」を確認します
  5. 目的のユーザーを見つけて、最初のステップから作成(取得)したグループに追加します

1
これは、Webアプリケーションのサービス/アプリプールアカウントに組み込みのアカウントを使用している場合に有効です。ドメインアカウントをサービス/アプリプールアカウントとして使用する場合、またはコード内でドメインアカウントを偽装する場合は、デフォルトで読み取り権限があり、この問題は発生しません。
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

翻訳がローカルで機能するがリモートのeiグループでは機能しない場合。Translate(typeof(NTAccount)

LOGGED IN USER IDを使用してアプリケーションコードを実行する場合は、偽装を有効にします。偽装は、IISを介して、またはweb.configに次の要素を追加することで有効にできます。

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

偽装が有効になっている場合、アプリケーションはユーザーアカウントにある権限を使用して実行されます。したがって、ログインしているユーザーが特定のネットワークリソースにアクセスできる場合、そのユーザーのみがアプリケーションを介してそのリソースにアクセスできます。

勤勉なビデオからのこの情報について、PRAGIM techに感謝します。

asp.netパート87でのWindows認証:

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

しかし、偽装はサーバーに多くのオーバーヘッドをもたらします

特定のネットワークグループのユーザーを許可する最適なソリューションは、Web構成で匿名を拒否することです <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.