UserPrincipals.GetAuthorizationGroups 枚举组时出错 (1301).升级到服务器 2

本文关键字:服务器 1301 枚举 GetAuthorizationGroups 出错 UserPrincipals | 更新日期: 2023-09-27 18:17:49

研究:

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

指向Microsoft端点更新是罪魁祸首的类似问题

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

背景:

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

我使用主体上下文进行表单身份验证。 用户名/传递握手成功,身份验证 cookie 设置正确,但后续也调用UserPrincipal.GetAuthorizationGroups主体上下文调用间歇性失败。 我们已经解决了服务器 2012 域控制器中出现的一些 BPA 问题,但这尚未解决问题。 我还建立了一个在两个独立服务器上运行的 cron。 尽管两台服务器运行相同的基本代码,但它们在不同时间将以组 SID 解析失败。 (开发环境和生产环境(。

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

此时,我正在尝试优化针对网络中特定域控制器以及新 DC 的 cron,并使用标准 LDAP 查询,该查询当前无法产生更有针对性的异常时间。 到目前为止,我们在一台 Web 服务器上发现,它失败的日子没有模式,但它会在大约 12 小时内恢复。 最新结果显示,组 SID 解析在上午 8 点到晚上 8 点之间失败,然后恢复,几天后它将在晚上 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<Principal> 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<Principal> 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;
    }

UserPrincipals.GetAuthorizationGroups 枚举组时出错 (1301).升级到服务器 2

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

经过一些实验,我们发现我们的代码在 Server 2012 上运行良好,但在客户端系统运行 Server 2008 时遇到 1301 错误代码。有关正在发生的事情的关键信息可在此处找到:

微软博客翻译自德语

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

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

希望这对某人有帮助!正如许多人所指出的那样,此方法调用似乎相当脆弱,我们可能会在遇到其他问题之前考虑实现一些替代方法。

加里

这是我的解决方案。它似乎一直工作得很好。由于迭代集合时出现问题,因此我在迭代时使用不同的方法,以便在不阻止实际迭代的情况下处理异常:

private string[] GetUserRoles(string Username)
{    
    List<string> roles = new List<string>();
    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<Principal> groups = user.GetAuthorizationGroups();
                int count = groups.Count();
                for (int i = 0; i < count; i++)
                {
                    IEnumerable<Principal> 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();
}

当我们的基础结构团队将 2012 域控制器联机时,我们遇到了此问题。 我们也有 2012 年之前的 DC,因此我们间歇性地遇到了这个问题。 我们想出了一个我想分享的修复程序 - 它有 2 个部分。

首先,安装加里希尔提到的修补程序。 这将解决以下问题:

枚举组时出错 (1301(。 无法解析该组的 SID。

我们以为安装此修补程序后我们是自由的。 但是,安装后,我们遇到了不同的间歇性错误。 我们正在审问的某些团体具有空sAMAccountName属性。 实际属性填充在 Active Directory 中,但 API 错误地返回了空值。 我认为这是Active Directory API中某个地方的错误,但我不知道更多。

幸运的是,我们能够通过切换到使用 group 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);
    }
}

希望有帮助。

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

我能够使用以下代码递归获取用户的组成员身份列表:

class ADGroupSearch
{
    List<String> groupNames;
    public ADGroupSearch()
    {
        this.groupNames = new List<String>();
    }
    public List<String> GetGroups()
    {
        return this.groupNames;
    }
    public void AddGroupName(String groupName)
    {
        this.groupNames.Add(groupName);
    }
    public List<String> 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<Principal> 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"并且您的用户名是"侯赛因",Windows通常会将您验证为GIS''Hussein。因此,您只需发送纯粹的用户名"侯赛因"。我解决了区分大小写的东西。
3(方法GetGroupsNew获取用户名并返回组列表
4(方法是Memberofnew获取用户名和一个组,并验证该用户是否属于该组,这是我感兴趣的用户。

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

我遇到了一个问题,如果我通过 VPN 连接并使用groups=UserPrincipal.GetGroups()则在迭代组时会发生异常。

如果有人想读取用户的所有组,则有以下可能性(这比使用GetGroups()更快(

private IList<string> GetUserGroupsLDAP(string samAccountName)
{
    var groupList = new List<string>();
    var domainConnection = new DirectoryEntry("LDAP://" + serverName, serverUser, serverUserPassword); // probably you don't need username and password
    var samSearcher = new DirectorySearcher();
    samSearcher.SearchRoot = domainConnection;
    samSearcher.Filter = "(samAccountName=" + samAccountName + ")";
    var samResult = samSearcher.FindOne();
    if (samResult != null)
    {
        var theUser = samResult.GetDirectoryEntry();
        theUser.RefreshCache(new string[] { "tokenGroups" });
        var sidSearcher = new DirectorySearcher();
        sidSearcher.SearchRoot = domainConnection;
        sidSearcher.PropertiesToLoad.Add("name");
        sidSearcher.Filter = CreateFilter(theUser);
        foreach (SearchResult result in sidSearcher.FindAll())
        {
            groupList.Add((string)result.Properties["name"][0]);
        }
    }
    return groupList;
}
private string CreateFilter(DirectoryEntry theUser)
{
    string filter = "(|";
    foreach (byte[] resultBytes in theUser.Properties["tokenGroups"])
    {
        var SID = new SecurityIdentifier(resultBytes, 0);
        filter += "(objectSid=" + SID.Value + ")";
    }
    filter += ")";
    return filter;
}
我们将

域控制器升级到 2012 后遇到了类似的问题。 突然我打电话给用户。GetAuthorizationGroups(( 开始失败;我遇到了与您相同的异常(错误 1301(。 所以,我把它改成了用户。GetGroups((。 这工作了一段时间,然后开始间歇性地失败"错误的用户名或密码"。 至少目前,我最新的解决方法似乎可以修复它。 在构造用户对象之后,我还构造了一个组对象,而不是调用其中任何一个,每个组对应一个,我想查看用户是否是其成员。 即,"用户。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;
        }
    }
}