UserPrincipals.GetAuthorizationGroups枚举组时发生错误(1301)。 升级到Server 2012域控制器后

研究:

与解决方法类似的问题,但不是现有问题的实际解决方案

类似的问题指向Microsoft End Point更新为罪魁祸首

上面的链接最适合我的问题,我也查看了Stack Overflow在创建这篇文章时列出的每个类似问题,只有上面引用的问题符合我的问题。

背景:

我一直在使用UserPrincipal.GetAuthorizationGroups获取在C#.NET 4.0 Web表单站点上的Server 2008 R2上运行IIS 7.5的特定页面访问权限2年半。 2013年5月15日,我们删除了运行Server 2008(而不是r2)的主域控制器,并将其替换为Server 2012域控制器。 第二天,我们开始收到下面列出的例外情况。

我使用Principal Context进行表单身份validation。 用户名/通过握手成功并且正确设置了auth cookie,但是后续的Principal Context调用也会调用UserPrincipal.GetAuthorizationGroups间歇性地失败。 我们已经解决了Server 2012域控制器中出现的一些BPA问题,但这还没有解决问题。 我还建立了一个在两个独立服务器上运行的cron。 虽然它们运行相同的代码库,但两个服务器将在不同的时间以组SID解析失败。 (开发环境和生产环境)。

该问题在Web服务器重启时暂时解决,并且在开发服务器上它将在12小时不能运行后自行解决。 生产服务器通常会停止正常运行,直到重新启动而不解决自身。

此时,我正在尝试优化针对网络中特定域控制器的cron以及新DC,并使用当前未能产生更多目标exception时间的标准LDAP查询。 到目前为止,我们已经在一个Web服务器上发现它失败的日子没有模式,但它将在大约12小时内恢复。 最新的结果显示,在上午8点到晚上8点之间,集团SID解决方案失败,然后恢复,几天后它将在晚上8点失败并在早上8点恢复,然后再运行12小时并再次失败。 我们希望看到它只是一个特定的服务器通信问题,还是看它是否是整个域控制器集。

例外:

 Exception information: Exception type: PrincipalOperationException Exception message: An error (1301) occurred while enumerating the groups. The group's SID could not be resolved. at System.DirectoryServices.AccountManagement.SidList.TranslateSids(String target, IntPtr[] pSids) at System.DirectoryServices.AccountManagement.SidList..ctor(SID_AND_ATTR[] sidAndAttr) at System.DirectoryServices.AccountManagement.AuthZSet..ctor(Byte[] userSid, NetCred credentials, ContextOptions contextOptions, String flatUserAuthority, StoreCtx userStoreCtx, Object userCtxBase) at System.DirectoryServices.AccountManagement.ADStoreCtx.GetGroupsMemberOfAZ(Principal p) at System.DirectoryServices.AccountManagement.UserPrincipal.GetAuthorizationGroups() 

题:

鉴于以上信息,有没有人知道为什么退出Windows Server 2008(而不是r2)并实施新的Server 2012 DC会导致UserPrincipal.GetAuthorizationGroups失败并出现1301 SID解析错误? 关于消除可能原因的想法也将受到赞赏。

免责声明:

这是我在Stack Overflow上的第一篇文章,我经常在这里研究,但直到现在才加入讨论。 请原谅我,如果我应该在其他地方发布,并在发布前随时指出更好的步骤。

2013年6月13日更新:

6月12日,我解决了未处理问题的可能性。 时间框架太短,无法确定调整后的代码是否已修复问题,但我会继续更新,因为我们正在努力达成一项解决方案,以便在这里有人可以随时提供帮助。

原始代码

  public bool isGroupMember(string userName, ArrayList groupList) { bool valid = false; PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domain_server + ".domain.org:636", null, ContextOptions.Negotiate | ContextOptions.SecureSocketLayer); // find the user in the identity store UserPrincipal user = UserPrincipal.FindByIdentity( ctx, userName); // get the groups for the user principal and // store the results in a PrincipalSearchResult object PrincipalSearchResult groups = user.GetAuthorizationGroups(); // display the names of the groups to which the // user belongs foreach (Principal group in groups) { foreach (string groupName in groupList) { if (group.ToString() == groupName) { valid = true; } } } return valid; } 

更新的代码

  public bool isGroupMember(string userName, ArrayList groupList, string domain_server) { bool valid = false; try { using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domain_server + ".domain.org:636", null, ContextOptions.Negotiate | ContextOptions.SecureSocketLayer)) { // find the user in the identity store UserPrincipal user = UserPrincipal.FindByIdentity( ctx, userName); try { // get the groups for the user principal and // store the results in a PrincipalSearchResult object using (PrincipalSearchResult groups = user.GetAuthorizationGroups()) { // display the names of the groups to which the // user belongs foreach (Principal group in groups) { foreach (string groupName in groupList) { if (group.ToString() == groupName) { valid = true; } } group.Dispose(); } }//end using-2 } catch { log_gen("arbitrary info"); return false; } }//end using-1 } catch { log_gen("arbitrary info"); return false; } return valid; } 

我刚刚遇到同样的问题,我设法追踪的信息可能会有所帮助; 如上所述,我们已经看到了域控制器运行Server 2012的问题 – 首先是客户部署,然后在我们自己的网络上复制。

经过一些实验,我们发现我们的代码在Server 2012上运行正常,但在客户端系统运行Server 2008时遇到了1301错误代码。关于发生了什么的关键信息在这里找到:

MS博客翻译自德语

下面的链接中提到的修补程序修复了我们的测试系统上的问题

SID S-1-18-1和SID S-1-18-2无法映射

希望这对某人有帮助! 正如许多人已经注意到这种方法调用似乎相当脆弱,我们可能会在遇到其他问题之前考虑实施一些替代方法。

加里

当我们的基础架构团队在线提供2012域控制器时,我们遇到了此问题。 我们还有2012年之前的DC,因此我们间歇性地遇到了这个问题。 我们提出了一个我想分享的修复方案 – 它有2个部分。

首先,安装Gary Hill提到的修补程序 。 这将解决以下问题:

枚举组时发生错误(1301)。 该组的SID无法解决。

安装此修补程序后,我们认为我们是免费的。 但是,在安装之后我们得到了一个不同的间歇性错误。 我们正在询问的某些组具有空的sAMAccountName属性。 实际属性填充在Active Directory中,但API错误地返回了空值。 我认为这是Active Directory API中的某个错误但我不知道更多。

幸运的是,我们可以通过切换到使用组Name属性而不是sAMAccountName属性来解决此问题。 这对我们有用。 我相信, sAMAccountName实际上已被弃用,仅出于向后兼容性原因而存在。 在这种情况下,这似乎是一个合理的改变。

我附上我们的GetRolesForUser代码的简化版本,以演示更改的位置。

 using (var context = new PrincipalContext(ContextType.Domain, _domainName)) { try { var p = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username); if (p == null) throw new NullReferenceException(string.Format("UserPrincipal.FindByIdentity returned null for user: {0}, this can indicate a problem with one or more of the AD controllers", username)); var groups = p.GetAuthorizationGroups(); var domain = username.Substring(0, username.IndexOf(@"\", StringComparison.InvariantCultureIgnoreCase)).ToLower(); foreach (GroupPrincipal group in groups) { if (!string.IsNullOrEmpty(group.Name)) { var domainGroup = domain + @"\" + group.Name.ToLower(); if (_groupsToUse.Any(x => x.Equals(domainGroup, StringComparison.InvariantCultureIgnoreCase))) { // Go through each application role defined and check if the AD domain group is part of it foreach (string role in roleKeys) { string[] roleMembers = new [] { "role1", "role2" }; foreach (string member in roleMembers) { // Check if the domain group is part of the role if (member.ToLower().Contains(domainGroup)) { // Cache the Application Role (NOT the AD role) results.Add(role); } } } } } group.Dispose(); } } catch (Exception ex) { throw new ProviderException("Unable to query Active Directory.", ex); } } 

希望有所帮助。

这是我的解决方案。 它似乎一直很好。 因为在迭代集合时会出现问题,所以我在迭代时使用不同的方法来处理exception而不阻塞实际的迭代:

 private string[] GetUserRoles(string Username) { List roles = new List(); try { string domain = Username.Contains("\\") ? Username.Substring(0, Username.IndexOf("\\")) : string.Empty; string username = Username.Contains("\\") ? Username.Substring(Username.LastIndexOf("\\") + 1) : Username; if (!string.IsNullOrEmpty(domain) && !string.IsNullOrEmpty(username)) { PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, domain); UserPrincipal user = UserPrincipal.FindByIdentity(principalContext, username); if (user != null) { PrincipalSearchResult groups = user.GetAuthorizationGroups(); int count = groups.Count(); for (int i = 0; i < count; i++) { IEnumerable principalCollection = groups.Skip(i).Take(1); Principal principal = null; try { principal = principalCollection.FirstOrDefault(); } catch (Exception e) { //Error handling... //Known exception - sometimes AD can't query a particular group, requires server hotfix? //http://support.microsoft.com/kb/2830145 } if (principal!=null && principal is GroupPrincipal) { GroupPrincipal groupPrincipal = (GroupPrincipal)principal; if (groupPrincipal != null && !string.IsNullOrEmpty(groupPrincipal.Name)) { roles.Add(groupPrincipal.Name.Trim()); } } } } } } catch (Exception e) { //Error handling... } return roles.ToArray(); } 

我在使用包含2个工作站和50个用户/组(其中许多是内置的)的全新虚拟开发域时遇到了UserPrincipal.GetAuthorizationGroups错误代码1301。 我们使用两个加入域的Windows 8.1 Enterprise工作站运行Windows Server 2012 R2 Essentials。

我能够使用以下代码以递归方式获取用户组成员资格的列表:

 class ADGroupSearch { List groupNames; public ADGroupSearch() { this.groupNames = new List(); } public List GetGroups() { return this.groupNames; } public void AddGroupName(String groupName) { this.groupNames.Add(groupName); } public List GetListOfGroupsRecursively(String samAcctName) { PrincipalContext ctx = new PrincipalContext(ContextType.Domain, System.Environment.UserDomainName); Principal principal = Principal.FindByIdentity(ctx, IdentityType.SamAccountName, samAcctName); if (principal == null) { return GetGroups(); } else { PrincipalSearchResult searchResults = principal.GetGroups(); if (searchResults != null) { foreach (GroupPrincipal sr in searchResults) { if (!this.groupNames.Contains(sr.Name)) { AddGroupName(sr.Name); } Principal p = Principal.FindByIdentity(ctx, IdentityType.SamAccountName, sr.SamAccountName); try { GetMembersForGroup(p); } catch (Exception ex) { //ignore errors and continue } } } return GetGroups(); } } private void GetMembersForGroup(Principal group) { if (group != null && typeof(GroupPrincipal) == group.GetType()) { GetListOfGroupsRecursively(group.SamAccountName); } } private bool IsGroup(Principal principal) { return principal.StructuralObjectClass.ToLower().Equals("group"); } } 

我在一个拥有多个域林和信任的环境中。 我几乎在网站表单上运行完全相同的代码,用于跨不同域执行用户安全组查找。

我在一个非常大的域中得到了这个确切的错误,其中组成员可以包含50多个不同的组。 它在其他域林中工作正常。

在我的研究中,我发现一个看起来不相关的线程,但实际上有相同的堆栈跟踪。 它适用于在SBS上运行的远程应用程序。 该线程提到错误是由组中无法解析的SIDS引起的。 我相信这些将是活动目录中所谓的“逻辑删除”SIDS。 请参阅此处的主题 。

线程表明,找到墓碑化的肠子并从群体中删除它们可以解决问题。 您收到的错误是否可能是因为SIDS通过单独的无关流程每12小时进行一次逻辑删除? 最终,我认为这是框架中的一个错误,并且该方法不会因为逻辑删除/无法解析的SIDS而崩溃。

祝好运!

如果有人感兴趣,这是相同代码的VB.NET版本。 在此代码可以工作之前,您无需执行任何操作

1)您必须引用程序集System.DirectoryServices
2)确保在没有域的情况下传递“theusername”变量,因此如果您的域名为“GIS”且您的用户名为“Hussein”,则Windows通常会将您validation为GIS \ Hussein。 所以你必须纯粹输入用户名“Hussein”。 我找出了区分大小写的东西。
3)方法GetGroupsNew获取用户名并返回组列表
4)方法是Memberofnew接受用户名和组并validation该用户是否属于该组,这是我感兴趣的用户。

 Private Function getGroupsNew(theusername As String) As List(Of String) Dim lstGroups As New List(Of String) Try Dim allDomains = Forest.GetCurrentForest().Domains.Cast(Of Domain)() Dim allSearcher = allDomains.[Select](Function(domain) Dim searcher As New DirectorySearcher(New DirectoryEntry("LDAP://" + domain.Name)) searcher.Filter = [String].Format("(&(&(objectCategory=person)(objectClass=user)(userPrincipalName=*{0}*)))", theusername) Return searcher End Function) Dim directoryEntriesFound = allSearcher.SelectMany(Function(searcher) searcher.FindAll().Cast(Of SearchResult)().[Select](Function(result) result.GetDirectoryEntry())) Dim memberOf = directoryEntriesFound.[Select](Function(entry) Using entry Return New With { _ Key .Name = entry.Name, _ Key .GroupName = DirectCast(entry.Properties("MemberOf").Value, Object()).[Select](Function(obj) obj.ToString()) _ } End Using End Function) For Each user As Object In memberOf For Each groupName As Object In user.GroupName lstGroups.Add(groupName) Next Next Return lstGroups Catch ex As Exception Throw End Try End Function Private Function isMemberofGroupNew(theusername As String, thegroupname As String) As Boolean Try Dim lstGroups As List(Of String) = getGroupsNew(theusername) For Each sGroup In lstGroups If sGroup.ToLower.Contains(thegroupname.ToLower) Then Return True Next Return False Catch ex As Exception Throw End Try End Function 

将域控制器升级到2012后,我们遇到了类似的问题。突然,我对user.GetAuthorizationGroups()的调用开始失败; 我得到了同样的例外(错误1301)。 所以,我把它改成了user.GetGroups()。 这工作了一段时间,然后开始间歇性地失败“坏用户名或密码”。 至少目前,我的最新解决方案似乎已经解决了这个问题。 在构造用户对象之后,我还没有调用其中任何一个,而是构建一个组对象,如果用户是其成员,我想要查看的每个组对应一个。 即,“user.IsMemberOf(group)”。 这似乎有效。

 try { using (HostingEnvironment.Impersonate()) { using (var principalContext = new PrincipalContext(ContextType.Domain, "MYDOMAIN")) { using (var user = UserPrincipal.FindByIdentity(principalContext, userName)) { if (user == null) { Log.Debug("UserPrincipal.FindByIdentity failed for userName = " + userName + ", thus not authorized!"); isAuthorized = false; } if (isAuthorized) { firstName = user.GivenName; lastName = user.Surname; // so this code started failing: // var groups = user.GetGroups(); // adGroups.AddRange(from @group in groups where // @group.Name.ToUpper().Contains("MYSEARCHSTRING") select @group.Name); // so the following workaround, which calls, instead, // "user.IsMemberOf(group)", // appears to work (for now at least). Will monitor for issues. // test membership in SuperUsers const string superUsersGroupName = "MyApp-SuperUsers"; using (var superUsers = GroupPrincipal.FindByIdentity(principalContext, superUsersGroupName)) { if (superUsers != null && user.IsMemberOf(superUsers)) // add to the list of groups this user is a member of // then do something with it later adGroups.Add(superUsersGroupName); } 

我有同样的例外。 如果有人不想使用“LDAP”,请使用此代码。 因为我有嵌套组,我使用了GetMembers(true),它的时间比GetMembers()稍长。

https://stackoverflow.com/a/27548271/1857271

或从此处下载修复程序: http : //support.microsoft.com/kb/2830145

面对同样的问题,枚举授权组和答案中提到的补丁不适用于我们的Web服务器。

然而,手动枚举和忽略导致故障的组的工作正常:

 private static bool UserIsMember(string usr, string grp) { usr = usr.ToLower(); grp = grp.ToLower(); using (var pc = new PrincipalContext(ContextType.Domain, "DOMAIN_NAME")) { using (var user = UserPrincipal.FindByIdentity(pc, usr)) { var isMember = false; var authGroups = user?.GetAuthorizationGroups().GetEnumerator(); while (authGroups?.MoveNext() ?? false) { try { isMember = authGroups.Current.Name.ToLower().Contains(grp); if (isMember) break; } catch { // ignored } } authGroups?.Dispose(); return isMember; } } }