【问题标题】:.NET 4.5 Bug in UserPrincipal.FindByIdentity (System.DirectoryServices.AccountManagement)UserPrincipal.FindByIdentity (System.DirectoryServices.AccountManagement) 中的 .NET 4.5 错误
【发布时间】:2012-09-18 12:09:20
【问题描述】:

在.NET 4.5 下测试我们的.NET 4.0 应用程序时,我们遇到了FindByIdentity 方法UserPrincipal 的问题。以下代码在 .NET 4.0 运行时运行时有效,但在 .NET 4.5 下失败:

[Test]
public void TestIsAccountLockedOut()
{
    const string activeDirectoryServer = "MyActiveDirectoryServer";
    const string activeDirectoryLogin = "MyADAccount@MyDomain";
    const string activeDirectoryPassword = "MyADAccountPassword";
    const string userAccountToTest = "TestUser@MyDomain";
    const string userPasswordToTest = "WRONGPASSWORD";

    var principalContext = new PrincipalContext(ContextType.Domain, activeDirectoryServer, activeDirectoryLogin, activeDirectoryPassword);

    var isAccountLockedOut = false;
    var isAuthenticated = principalContext.ValidateCredentials(userAccountToTest, userPasswordToTest, principalContext.Options);
    if (!isAuthenticated)
    {
        // System.DirectoryServices.AccountManagement.PrincipalOperationException : Information about the domain could not be retrieved (1355).
        using (var user = UserPrincipal.FindByIdentity(principalContext, IdentityType.UserPrincipalName, userAccountToTest))
        {
            isAccountLockedOut = (user != null) && user.IsAccountLockedOut();
        }
    }
    Assert.False(isAuthenticated);
    Assert.False(isAccountLockedOut);
}

这是异常堆栈跟踪:

System.DirectoryServices.AccountManagement.PrincipalOperationException : Information about the domain could not be retrieved (1355).
at System.DirectoryServices.AccountManagement.Utils.GetDcName(String computerName, String domainName, String siteName, Int32 flags)   at System.DirectoryServices.AccountManagement.ADStoreCtx.LoadDomainInfo()   at 
System.DirectoryServices.AccountManagement.ADStoreCtx.get_DnsDomainName()   at System.DirectoryServices.AccountManagement.ADStoreCtx.GetAsPrincipal(Object storeObject, Object discriminant)   at 
System.DirectoryServices.AccountManagement.ADStoreCtx.FindPrincipalByIdentRefHelper(Type principalType, String urnScheme, String urnValue, DateTime referenceDate, Boolean useSidHistory)   at 
System.DirectoryServices.AccountManagement.ADStoreCtx.FindPrincipalByIdentRef(Type principalType, String urnScheme, String urnValue, DateTime referenceDate)   at 
System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithTypeHelper(PrincipalContext context, Type principalType, Nullable`1 identityType, String identityValue, DateTime refDate)   at 
System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithType(PrincipalContext context, Type principalType, IdentityType identityType, String identityValue)   at 
System.DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity(PrincipalContext context, IdentityType identityType, String identityValue)   

有没有其他人看到并解决了这个问题?如果没有,我们是否有更好的方法来检查 Active Directory 帐户的IsAccountLockedOut 状态?

作为参考,我们所有的测试机器都在同一个子网内。有不同的 ActiveDirectory 服务器运行 Windows Server 2003、2008 和 2012,在各种域功能模式下(见下文)。该代码在运行 .NET 4.0 的机器上运行,但在运行 .NET 4.5 的机器上运行失败。

我们运行代码的三台 .NET 机器是:
- 运行 .NET 4.0 的 Windows 7
- 运行 .NET 4.5 的 Windows Vista
- 运行 .NET 4.5 的 Windows Server 2012

我们尝试过的 Active Directory 服务器是:
- 将 AD 域功能模式设置为 Windows 2000 本机的 Windows 2003
- 将 AD 域功能模式设置为 Windows Server 2003 的 Windows 2003
- 将 AD 域功能模式设置为 Windows 2000 本机的 Windows 2008
- 将 AD 域功能模式设置为 Windows Server 2003 的 Windows 2008
- 将 AD 域功能模式设置为 Windows Server 2008 的 Windows 2008
- 将 AD 域功能模式设置为 Windows 2012 的 Windows 2012

所有这些 Active Directory 服务器都配置为一个简单的单一林,并且客户端计算机不是域的一部分。除了测试此行为之外,它们不用于任何其他功能,并且除了 Active Directory 之外不运行任何其他功能。


编辑 - 2012 年 10 月 9 日

感谢所有回复的人。下面是演示该问题的 C# 命令行客户端,以及我们确定的短期解决方法,它不需要我们更改有关 Active Directory 和 DNS 配置的任何内容。似乎该异常仅在 PrincipalContext 的实例中引发一次。我们包含了 .NET 4.0 机器 (Windows 7) 和 .NET 4.5 机器 (Windows Vista) 的输出。

using System;
using System.DirectoryServices.AccountManagement;

namespace ADBug
{
    class Program
    {
        static void Main(string[] args)
        {
            const string activeDirectoryServer = "MyActiveDirectoryServer";
            const string activeDirectoryLogin = "MyADAccount";
            const string activeDirectoryPassword = "MyADAccountPassword";
            const string validUserAccount = "TestUser@MyDomain.com";
            const string unknownUserAccount = "UnknownUser@MyDomain.com";

            var principalContext = new PrincipalContext(ContextType.Domain, activeDirectoryServer, activeDirectoryLogin, activeDirectoryPassword);

            // .NET 4.0 - First attempt with a valid account finds the user
            // .NET 4.5 - First attempt with a valid account fails with a PrincipalOperationException
            TestFindByIdentity(principalContext, validUserAccount, "Valid Account - First Attempt");
            // Second attempt with a valid account finds the user
            TestFindByIdentity(principalContext, validUserAccount, "Valid Account - Second Attempt");
            // First attempt with an unknown account does not find the user
            TestFindByIdentity(principalContext, unknownUserAccount, "Unknown Account - First Attempt");
            // Second attempt with an unknown account does not find the user (testing false positive)
            TestFindByIdentity(principalContext, unknownUserAccount, "Unknown Account - Second Attempt");
            // Subsequent attempt with a valid account still finds the user
            TestFindByIdentity(principalContext, validUserAccount, "Valid Account - Third Attempt");
        }

        private static void TestFindByIdentity(PrincipalContext principalContext, string userAccountToTest, string message)
        {
            var exceptionThrown = false;
            var userFound = false;
            try
            {
                using (var user = UserPrincipal.FindByIdentity(principalContext, IdentityType.UserPrincipalName, userAccountToTest))
                {
                    userFound = (user != null);
                }
            }
            catch (PrincipalOperationException)
            {
                exceptionThrown = true;
            }
            Console.Out.WriteLine(message + " - Exception Thrown  = {0}", exceptionThrown);
            Console.Out.WriteLine(message + " - User Found = {1}", userAccountToTest, userFound);
        }
    }
}

.NET 4.0 输出

Valid Account - First Attempt - Exception Thrown  = False
Valid Account - First Attempt - User Found = True
Valid Account - Second Attempt - Exception Thrown  = False
Valid Account - Second Attempt - User Found = True
Unknown Account - First Attempt - Exception Thrown  = False
Unknown Account - First Attempt - User Found = False
Unknown Account - Second Attempt - Exception Thrown  = False
Unknown Account - Second Attempt - User Found = False
Valid Account - Third Attempt - Exception Thrown  = False
Valid Account - Third Attempt - User Found = True

.NET 4.5 输出

Valid Account - First Attempt - Exception Thrown  = True
Valid Account - First Attempt - User Found = False
Valid Account - Second Attempt - Exception Thrown  = False
Valid Account - Second Attempt - User Found = True
Unknown Account - First Attempt - Exception Thrown  = False
Unknown Account - First Attempt - User Found = False
Unknown Account - Second Attempt - Exception Thrown  = False
Unknown Account - Second Attempt - User Found = False
Valid Account - Third Attempt - Exception Thrown  = False
Valid Account - Third Attempt - User Found = True

【问题讨论】:

  • 不确定您是否已经在 google 上搜索过,但我发现了一篇包含几个 cmets 的帖子,用于修复可能导致此问题的各种场景:elegantcode.com/2009/03/21/… 我自己从未见过,但在有兴趣提供帮助,我想我会分享这个,因为它看起来对我有用。
  • 例如您是否尝试过传入 DC 和 CN 例如 PrincipalContext ctx = new PrincipalContext( ContextType.Domain, "fabrikam.com", "CN=Users,DC=fabrikam,DC=com", "管理员", "securelyStoredPassword");
  • DJ,我们刚刚尝试传入 CN & DC 字符串,在 .NET 4.0 下仍然有效,在 .NET 4.5 下失败。
  • 只是想确认您不是唯一遇到此问题的人。我有一段几乎相同的代码,它在 FindByIdentity 处引发了相同的异常。我已经在三台具有相同网络设置的计算机上运行它,只有一台具有 .NET 4.5 的计算机出现故障。我不会很快将错误声明为外部错误,但它在升级之前确实有效。
  • 我也确认了这个问题。使用 vmware 快照反弹系统前/后 .net 4.5 安装,以验证问题开始的时间。我们还从不在被查询域上的机器进行查询。简单地安装 .net 4.5 会导致工作代码开始无法针对 .net 4 或 4.5。我们正在使用针对 .net 4.0 的 vs2010。将目标更改为 3.5,代码甚至在 4.5 安装后也能正常工作。

标签: c# .net authentication active-directory .net-4.5


【解决方案1】:

我们遇到了完全相同的问题(跨域查询在更新到 4.5 时失败) - 我认为这是一个错误,因为它破坏了现有 (4.0) 代码。

但是,为了使其正常工作 - 查看其中一个(现在)失败的客户端,我注意到有一堆失败的 SRV 记录的 DNS 请求,格式如下:

_ldap._tcp.MYSERVER1.mydomain.com,INet,Srv
_ldap._tcp.dc._msdcs.mydomain.com,INet,Srv

修改我们的 DNS 服务器(故障客户端使用的 DNS)为所有 mydomain.com 流量到域中的一个 DC 的转发区域确实解决了问题。

使用 nslookup,从之前(失败时)到现在(工作)的行为是,之前这些查询将返回“不存在的域”,而现在它们返回“* 无服务位置(SRV) 记录可用于..."。失败点似乎是域的感知不存在,而不是缺少 SRV 记录。希望 MS 能够恢复这种行为,但与此同时,如果您可以控制故障客户端的 DNS,您可能会幸运地创建 DNS 转发区域。

【讨论】:

    【解决方案2】:

    对于 OP(以及其他帮助回复的人),我们有(遇到)同样的问题。在我们的开发环境中,安装了 VS2012 并且我们的应用程序在登录期间在运行时中断(如上所述的 AD 问题)。所以我清理了我的系统并继续使用 2010 年,每次我读到一篇关于 2012 年有多棒的新博文时都会流泪。

    感谢 Scott Hanselman,我找到了这个帖子。我在我的开发盒上安装了一个VM,上面安装了Windows 8 developer 90day preview,还有VS2012。让我们的应用程序启动并运行,并立即遇到登录 AD 障碍。只需将我们的 FindByIdentity 包装在 try catch 中,并在第一次 catch 后强制它再次尝试 - 中提琴它起作用了!所以感谢那些想出这个小技巧的人!

    因此,它是一个小修复,是一个适用于本地开发的“hack”,并且不应该影响生产,因为我们不会很快将 4.5 投入生产。

    但缺点是在本地,现在登录需要 2 分钟,而我们在 2010 年以下运行时需要几秒钟:(

    我真的不知道我还能提供什么来实际尝试帮助解决这种情况,但无论如何我想我会分享我的 2 美分,因为这似乎仍然是一个主要问题。

    【讨论】:

    • 感谢您的反馈。我们确认了相同的行为(第一次尝试失败,随后都成功)并在上面发布了一个测试客户端。我们从 Microsoft 收到的最后反馈如下:“.Net4.5 中的 SDS.AM 添加了一个限制,要求在客户端进行域 DNS 解析。这被设计为 DNS 最佳实践应该具有正确的 DNS 条目。基于根据论坛的反馈,我们正在考虑在未来的版本中取消限制。”
    • 谢谢!我还注意到的一件事是,在添加第一次尝试失败捕获后 - 对 AD 的任何调用也会有相当大的延迟 - 通话 3 分钟以上。我可以添加本地主机的条目来解决此类问题吗?再次感谢您的反馈!
    • 我不确定这是否相关,但在使用主机名(例如 myserver)而不是服务器的 IP 地址(例如 192.168.14.3)时,我们总是遇到严重的性能问题。我们还没有对这个问题的 try-catch 变通方法进行任何性能测试,但我会整理一个测试用例,看看我们能得出什么样的数字。
    • 不错!!尝试捕获两次 FindByIdentity() 调用! -谢谢
    【解决方案3】:

    将 .net 框架从 4.0 升级到 4.5 后遇到同样的问题 我已经将框架升级到 .net 4.5.1 并且它有效。

    【讨论】:

    • 我遇到了与IsMemberOf() 方法类似的问题。我的 .net 版本是 4.5.2,但 DNS 似乎是硬性要求,我的测试机器无法满足。任何解决方法?
    • 我在这里开了一个新帖子stackoverflow.com/questions/52242019/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多