如何在Active Directory中获取用户组? (c#,asp.net)

时间:2011-03-15 09:50:20

标签: c# asp.net active-directory

我使用此代码获取当前用户的组。但我想手动给用户,然后得到他的组。我怎么能这样做?

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

9 个答案:

答案 0 :(得分:148)

如果您使用的是.NET 3.5或更高版本,则可以使用新的System.DirectoryServices.AccountManagement(S.DS.AM)命名空间,这使得它比以前容易得多。

在此处阅读所有相关内容: Managing Directory Security Principals in the .NET Framework 3.5

更新:旧的MSDN杂志文章不再在线,不幸的是 - 您需要来自Microsoft的download the CHM for the January 2008 MSDN magazine并阅读其中的文章。

基本上,您需要拥有“主要上下文”(通常是您的域名),用户主体,然​​后您可以非常轻松地获得其组:

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

答案 1 :(得分:52)

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因为我在一个非常大的AD中有200个组中的2个有一些例外,因为某些SID不再可用。 (Translate()调用执行SID - &gt;名称转换。)

答案 2 :(得分:15)

首先,GetAuthorizationGroups()是一个很棒的功能,但不幸的是有两个缺点:

  1. 性能很差,特别是在拥有众多用户和群组的大公司中。它会获取您实际需要的更多数据,并在结果
  2. 中为每个循环迭代执行服务器调用
  3. 它包含可能导致您的应用程序停止工作的错误。当团体和用户不断发展时。 Microsoft认识到了这个问题并且与某些SID相关。您得到的错误是&#34;枚举组时发生错误&#34;
  4. 因此,我编写了一个小函数来替换具有更好性能和错误安全性的GetAuthorizationGroups()。它只使用索引字段进行一次LDAP调用。如果您需要的属性多于组名(&#34; cn&#34;属性),则可以轻松扩展。

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

答案 3 :(得分:10)

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

答案 4 :(得分:1)

在我的情况下,我可以继续使用GetGroups()而没有任何expcetion的唯一方法是将用户(USER_WITH_PERMISSION)添加到有权读取AD(Active Directory)的组中。构建传递此用户和密码的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创建一个组(或采取一个组)并在secutiry选项卡下添加&#34; Windows授权访问组&#34;
  2.   
  3. 点击&#34;高级&#34;按钮
  4.   
  5. 选择&#34; Windows授权访问组&#34;并点击&#34;查看&#34;
  6.   
  7. 检查&#34;读取tokenGroupsGlobalAndUniversal&#34;
  8.   
  9. 找到所需的用户并添加到您从第一步创建(拍摄)的组
  10.   

答案 5 :(得分: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());
}

答案 6 :(得分:0)

如果Translate在本地工作但不能远程工作e.i group。翻译(typeof(NTAccount)

如果要使用LOGGED IN USER标识执行应用程序代码,请启用模拟。可以通过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

注意:小组必须用反斜杠写,即&#34; TheDomain \ TheGroup&#34;

答案 7 :(得分:0)

这对我有用

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

答案 8 :(得分:0)

答案取决于您要检索的组的类型。 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();