1

这是对我之前的问题的跟进。

问题

验证传递给 a 的凭据的正确方法是PrincipalContext什么?

背景

在我的应用程序中,我实例化了一个PrincipalContextusing PrincipalContext(ContextType, String, String, String). 当凭据不正确(或提供的凭据不适用于管理员)时,我有许多集成测试失败,因此我希望能够捕捉到这一点。

如果凭证无效,则PrincipalContext.ConnectedServer抛出一个System.DirectoryServices.DirectoryServicesCOMException,但是直到第一次使用 PrincipalContext 才发现。

try
{
    PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "my_domain.local", "wrong_username", "wrong_password");
}
catch (exception e)
{
    // This block is not hit
}

// `System.DirectoryServices.DirectoryServicesCOMException` raised here
using (UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, samAccountName)) {}

异常详情:

System.DirectoryServices.DirectoryServicesCOMException
  HResult=0x8007052E
  Message=The user name or password is incorrect.

  Source=System.DirectoryServices
  StackTrace:
   at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
   at System.DirectoryServices.DirectoryEntry.Bind()
   at System.DirectoryServices.DirectoryEntry.get_AdsObject()
   at System.DirectoryServices.PropertyValueCollection.PopulateList()
   at System.DirectoryServices.PropertyValueCollection..ctor(DirectoryEntry entry, String propertyName)
   at System.DirectoryServices.PropertyCollection.get_Item(String propertyName)
   at System.DirectoryServices.AccountManagement.PrincipalContext.DoLDAPDirectoryInitNoContainer()
   at System.DirectoryServices.AccountManagement.PrincipalContext.DoDomainInit()
   at System.DirectoryServices.AccountManagement.PrincipalContext.Initialize()
   at System.DirectoryServices.AccountManagement.PrincipalContext.get_QueryCtx()
   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)

我试过的

我最初的想法是在创建时检查凭据,但是如果我们使用PrincipalContext不同的凭据重用,我们会得到一个System.DirectoryServices.Protocols.LdapException.

PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "my_domain.local", "correct_username", "correct_password");
if (ctx.ValidateCredentials("correct_username", "correct_password"))
{
    // `System.DirectoryServices.Protocols.LdapException` raised here
    using (UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, different_user)) {}
}

异常详情:

System.DirectoryServices.Protocols.LdapException
  HResult=0x80131500
  Message=The LDAP server is unavailable.

  Source=System.DirectoryServices.Protocols
  StackTrace:
   at System.DirectoryServices.Protocols.ErrorChecking.CheckAndSetLdapError(Int32 error)
   at System.DirectoryServices.Protocols.LdapSessionOptions.FastConcurrentBind()
   at System.DirectoryServices.AccountManagement.CredentialValidator.BindLdap(NetworkCredential creds, ContextOptions contextOptions)
   at System.DirectoryServices.AccountManagement.CredentialValidator.Validate(String userName, String password)
   at System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials(String userName, String password)

什么是正确的方法?

有没有公​​认的方法来测试这个?我应该尝试分配PrincipalContext.ConnectedServer给局部变量并捕获异常吗?

4

1 回答 1

1

您可以将上下文的实际使用移动到try块中:

try
{
    PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "my_domain.local", "wrong_username", "wrong_password");
    using (UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, samAccountName)) {}
}
catch (exception e)
{

}

如果您打算将该上下文用于其他操作,那么这是我可以看到如何测试凭据的唯一方法。

但是,如果您的唯一目标是验证凭据,那么您可以DirectoryEntry直接使用(System.DirectoryServices从 NuGet 安装)。无论如何,您将从下面PrincipalContext使用的堆栈跟踪中看到。DirectoryEntry我发现DirectoryEntry直接使用要快得多,尽管有时使用起来会更复杂。

以下是您将如何验证凭据DirectoryEntry

var entry = new DirectoryEntry("LDAP://domain.local", "username", "password");
//creating the object doesn't actually make a connection, so we have to do something to test it
try {
    //retrieve only the 'cn' attribute from the object
    entry.RefreshCache(new[] {"cn"});
} catch (Exception e) {

}

另一种方法是LdapConnection直接使用(System.DirectoryServices.Protocols从 NuGet 安装)。这可能是验证凭据所需的最少实际网络流量。但是您可能必须弄清楚身份验证方法。默认情况下它使用协商,但如果这不起作用,您将不得不使用不同的构造函数并手动选择身份验证方法。

var id = new LdapDirectoryIdentifier("domain.local");
var conn = new LdapConnection(id, new NetworkCredential("username", "password"));
try {
    conn.Bind();
} catch (Exception e) {

}
于 2018-04-30T12:57:28.470 回答