C# 使用C查找递归组成员身份(Active Directory)#
我希望在Active Directory中获得用户所属的所有组的列表,这些组既显式地列在memberOf属性列表中,也隐式地通过嵌套组成员身份列出。例如,如果我检查UserA,并且UserA是GroupA和GroupB的一部分,那么如果GroupB是GroupC的成员,我还想列出GroupC 为了让您对我的应用程序有更多的了解,我将在有限的基础上进行此操作。基本上,我希望偶尔进行一次安全检查,列出这些额外的会员资格。我想区分两者,但这并不难 我的问题是,我还没有找到一种有效的方法来实现这个查询。Active Directory()上的标准文本显示了一种基本上是递归查找的方法。这似乎效率极低。即使在我的小域名中,用户也可能拥有30多个组成员。这意味着一个用户可以调用30多次Active Directory 我研究了以下LDAP代码,以一次获取所有memberOf条目:C# 使用C查找递归组成员身份(Active Directory)#,c#,.net,active-directory,C#,.net,Active Directory,我希望在Active Directory中获得用户所属的所有组的列表,这些组既显式地列在memberOf属性列表中,也隐式地通过嵌套组成员身份列出。例如,如果我检查UserA,并且UserA是GroupA和GroupB的一部分,那么如果GroupB是GroupC的成员,我还想列出GroupC 为了让您对我的应用程序有更多的了解,我将在有限的基础上进行此操作。基本上,我希望偶尔进行一次安全检查,列出这些额外的会员资格。我想区分两者,但这并不难 我的问题是,我还没有找到一种有效的方法来实现这个查询。
(memberOf:1.2.840.113556.1.4.1941:={0})
(memberOf:1.2.840.113556.1.4.1941:={0})
其中{0}将是我的LDAP路径(例如:CN=UserA,OU=Users,DC=foo,DC=org)。但是,它不会返回任何记录。这种方法的缺点是,即使有效,我也不知道哪一组是显式的,哪一组是隐式的
这就是我目前所拥有的。我想知道是否有比CodeProject文章更好的方法,如果是的话,如何实现(实际的代码会很棒)。我正在使用.NET4.0和C#。我的Active Directory处于Windows 2008功能级别(它还不是R2)。如果除了递归调用之外没有其他方法(我相信没有),那么至少可以让框架为您完成工作:请参阅(在
System.DirectoryServices.AccountManagement
命名空间中,并在.Net 3.5中介绍)
此方法搜索所有组
以递归方式返回组
用户是其中的一个成员。这个
返回的集合还可能包括
该系统将支持的其他组
考虑用户的成员
授权目的
将结果与(“返回指定当前主体是其成员的组的组对象集合”)进行比较,以查看成员身份是显式的还是隐式的。谢谢这是一个有趣的问题 接下来,只是一个更正,你说: 我研究了以下LDAP代码,以一次获取所有memberOf条目:
(memberOf:1.2.840.113556.1.4.1941:={0})
(memberOf:1.2.840.113556.1.4.1941:={0})
你不能让它工作。我记得当我知道它的存在时,我让它工作了,但它是在一个LDIFDE.EXE过滤器中。所以我把它应用到C#中的ADSI中,它仍然在工作。我从Microsoft获取的示例中有太多的括号,但它仍然有效()
根据您关于我们不知道用户是否明确属于该组这一事实的评论,我再添加一个请求。我知道这不是很好,但这是我能做的最好的了
static void Main(string[] args)
{
/* Connection to Active Directory
*/
DirectoryEntry deBase = new DirectoryEntry("LDAP://WM2008R2ENT:389/dc=dom,dc=fr");
/* To find all the groups that "user1" is a member of :
* Set the base to the groups container DN; for example root DN (dc=dom,dc=fr)
* Set the scope to subtree
* Use the following filter :
* (member:1.2.840.113556.1.4.1941:=cn=user1,cn=users,DC=x)
*/
DirectorySearcher dsLookFor = new DirectorySearcher(deBase);
dsLookFor.Filter = "(member:1.2.840.113556.1.4.1941:=CN=user1 Users,OU=MonOu,DC=dom,DC=fr)";
dsLookFor.SearchScope = SearchScope.Subtree;
dsLookFor.PropertiesToLoad.Add("cn");
SearchResultCollection srcGroups = dsLookFor.FindAll();
/* Just to know if user is explicitly in group
*/
foreach (SearchResult srcGroup in srcGroups)
{
Console.WriteLine("{0}", srcGroup.Path);
foreach (string property in srcGroup.Properties.PropertyNames)
{
Console.WriteLine("\t{0} : {1} ", property, srcGroup.Properties[property][0]);
}
DirectoryEntry aGroup = new DirectoryEntry(srcGroup.Path);
DirectorySearcher dsLookForAMermber = new DirectorySearcher(aGroup);
dsLookForAMermber.Filter = "(member=CN=user1 Users,OU=MonOu,DC=dom,DC=fr)";
dsLookForAMermber.SearchScope = SearchScope.Base;
dsLookForAMermber.PropertiesToLoad.Add("cn");
SearchResultCollection memberInGroup = dsLookForAMermber.FindAll();
Console.WriteLine("Find the user {0}", memberInGroup.Count);
}
Console.ReadLine();
}
在我的测试树中,给出:
LDAP://WM2008R2ENT:389/CN=MonGrpSec,OU=MonOu,DC=dom,DC=fr
adspath : LDAP://WM2008R2ENT:389/CN=MonGrpSec,OU=MonOu,DC=dom,DC=fr
cn : MonGrpSec
Find the user 1
LDAP://WM2008R2ENT:389/CN=MonGrpDis,OU=ForUser1,DC=dom,DC=fr
adspath : LDAP://WM2008R2ENT:389/CN=MonGrpDis,OU=ForUser1,DC=dom,DC=fr
cn : MonGrpDis
Find the user 1
LDAP://WM2008R2ENT:389/CN=MonGrpPlusSec,OU=ForUser1,DC=dom,DC=fr
adspath : LDAP://WM2008R2ENT:389/CN=MonGrpPlusSec,OU=ForUser1,DC=dom,DC=fr
cn : MonGrpPlusSec
Find the user 0
LDAP://WM2008R2ENT:389/CN=MonGrpPlusSecUniv,OU=ForUser1,DC=dom,DC=fr
adspath : LDAP://WM2008R2ENT:389/CN=MonGrpPlusSecUniv,OU=ForUser1,DC=dom,DC=fr
cn : MonGrpPlusSecUniv
Find the user 0
(编辑)
“1.2.840.113556.1.4.1941”在W2K3 SP1中不起作用,它开始在SP2中起作用。我想这和W2K3R2是一样的。它应该在W2K8上工作。我在这里测试W2K8R2。我很快就能在W2K8上测试这一点。递归使用ldap过滤器,但每次查询后返回的所有组的查询都会减少往返次数 例:
- 确保只拉属性 你需要回来李>
- 缓存结果可以极大地帮助您 性能,但使我的代码 更复杂李>
- 确保使用连接池
- 第一组必须单独处理
静态列表和查找所有成员(字符串a\u sSearchRoot、字符串a\u sGroupDN、字符串[]a\u asPropsToLoad)
{
使用(DirectoryEntry de=newdirectoryEntry(a_-sSearchRoot))
返回ad_find_all_成员(de、a_sGroupDN、a_asPropsToLoad);
}
静态列表ad_find_all_成员(目录条目a_SearchRoot,字符串a_sGroupDN,字符串[]a_asPropsToLoad)
{
字符串sDN=“DifferentiedName”;
字符串sOC=“objectClass”;
字符串sOC_GROUP=“GROUP”;
字符串[]asPropsToLoad=a_asPropsToLoad;
Array.Sort(asPropsToLoad);
if(Array.BinarySearch(asPropsToLoad,sDN)<0)
{
调整数组大小(参考asPropsToLoad,asPropsToLoad.Length+1);
asPropsToLoad[asPropsToLoad.Length-1]=sDN;
}
if(Array.BinarySearch(asPropsToLoad,sOC)<0)
{
调整数组大小(参考asPropsToLoad,asPropsToLoad.Length+1);
asPropsToLoad[asPropsToLoad.Length-1]=sOC;
}
List lsr=新列表();
使用(DirectorySearcher ds=新的DirectorySearcher(a_SearchRoot))
{
ds.Filter=“(&(|(objectClass=group)(objectClass=user))(memberOf=“+a_sGroupDN+”)”;
ds.PropertiesToLoad.Clear();
ds.PropertiesToLoad.AddRange(asPropsToLoad);
ds.PageSize=1000;
ds.SizeLimit=0;
foreach(ds.FindAll()中的SearchResult sr)
lsr.Add(sr);
}
对于(int i=0;i,如果您在Exchange server上,则可以使用令牌组和令牌组全局通用属性。
令牌组将为您提供该用户所属的所有安全组,包括嵌套组和域用户、用户等
tokenGroupsGlobalAndUniversal将包括从tokenGroups到distribution Group的所有内容
private void DoWorkWithUserGroups(string domain, string user)
{
var groupType = "tokenGroupsGlobalAndUniversal"; // use tokenGroups for only security groups
using (var userContext = new PrincipalContext(ContextType.Domain, domain))
{
using (var identity = UserPrincipal.FindByIdentity(userContext, IdentityType.SamAccountName, user))
{
if (identity == null)
return;
var userEntry = identity.GetUnderlyingObject() as DirectoryEntry;
userEntry.RefreshCache(new[] { groupType });
var sids = from byte[] sid in userEntry.Properties[groupType]
select new SecurityIdentifier(sid, 0);
foreach (var sid in sids)
{
using(var groupIdentity = GroupPrincipal.FindByIdentity(userContext, IdentityType.Sid, sid.ToString()))
{
if(groupIdentity == null)
continue; // this group is not in the domain, probably from sidhistory
// extract the info you want from the group
}
}
}
}
}
如果您使用的是.NET 3.5或更高版本,则可以使用System.DirectoryServices.AccountManagement
名称空间,这样做非常简单
请参见此处的相关答案: