30

.NET 4.5 で .NET 4.0 アプリケーションをテストしたところ、.NET の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)   

他の誰かがこの問題を見て解決しましたか? IsAccountLockedOutそうでない場合、Active Directory アカウントのステータスを確認するためのより良い方法はありますか?

参考までに、すべてのテスト マシンは同じサブネット内にあります。さまざまなドメイン機能モードで、Windows Server 2003、2008、および 2012 を実行する個別の ActiveDirectory サーバーがあります (以下を参照)。このコードは、.NET 4.0 を実行しているマシンからは機能しますが、.NET 4.5 を実行しているマシンからは失敗します。

コードを実行した 3 台の .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 のインスタンスで 1 回だけスローされるようです。.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
4

3 に答える 3

12

まったく同じ問題が発生しています (4.5 への更新時にクロス ドメイン クエリが失敗する) - 既存の (4.0) コードを壊すため、これはバグと考えます。

ただし、それを機能させるために、(現在) 失敗しているクライアントの 1 つを調べたところ、失敗している SRV レコードに対する次の形式の DNS 要求が多数あることに気付きました。

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

DNS サーバー (失敗したクライアントが使用する DNS) を変更して、ドメイン上の DC の 1 つへのすべての mydomain.com トラフィックの転送ゾーンを持つようにすると、問題は解決しました。

nslookup を使用すると、以前 (失敗していたとき) から現在 (動作中) までの動作は、これらのクエリが "存在しないドメイン" を返す前に、" * No Service location (SRV) records available for ..."を返すというものでした。 . 失敗のポイントは、SRV レコードの欠落ではなく、ドメインが存在しないと認識されているようです。MS がこの動作を元に戻すことを願っていますが、障害のあるクライアントの DNS を制御できる場合は、それまでの間、DNS 転送ゾーンを作成することができるかもしれません。

于 2012-10-08T20:42:13.720 に答える
6

OP(および返信を手伝ってくれた他の人)には、まったく同じ問題があります。私たちの開発環境では、VS2012 をインストールし、アプリはログイン時に実行時に壊れました (上記で指摘した AD の問題)。そのため、システムを消去して 2010 を使い続けましたが、2012 年の素晴らしさについての新しいブログ記事を読むたびに涙を流していました。

Scott Hanselman のおかげで、このスレッドを見つけました。開発ボックスに VM をインストールし、その上に Windows 8 開発者 90 日プレビュー、および VS2012 をインストールしました。アプリケーションを起動して実行すると、すぐにログイン AD 障害が発生しました。FindByIdentity を try キャッチでラップし、最初のキャッチの後に再試行を強制するだけで、ビオラは機能します!! だから、その小さなトリックを考え出した人に感謝します!!

したがって、これはマイナーな修正であり、ローカル開発で機能する「ハック」であり、4.5 をすぐに本番環境に導入する予定はないため、本番環境に影響を与えるべきではありません。

しかし、欠点は、ローカルでのログインに、2010 で実行したときの数秒から 2 分ほどかかることです:(

状況を実際に解決するために他に何を提供できるかはよくわかりませんが、これはまだ大きな問題であるように思われるので、とにかく私の 2 セントを共有することにしました。

于 2012-11-09T20:36:53.227 に答える