如何验证传递给“PrincipalContext"的凭据 [英] How to validate the credentials passed to a `PrincipalContext`

查看:29
本文介绍了如何验证传递给“PrincipalContext"的凭据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

What is the correct way of validating the credentials passed to a PrincipalContext?

背景

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

Background

In my application I instantiate a PrincipalContext using PrincipalContext(ContextType, String, String, String). I have a number of integration tests that fail when the credentials are incorrect (or the supplied credentials are not for an admin) so I want to be able to catch this.

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

If the credentials are invalid PrincipalContext.ConnectedServer throws a System.DirectoryServices.DirectoryServicesCOMException, however this is not discovered until the first use of the 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.>

What I tried

My initial thought was to check the credentials on creation, however if we reuse the PrincipalContext with different credentials we get a 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 分配给局部变量并捕获异常吗?

What is the correct approach?

Is there an accepted way to test this? Should I try to assign PrincipalContext.ConnectedServer to a local variable and catch an exception?

推荐答案

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

You could just move the actual usage of the context into the try block:

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)
{

}

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

If you're planning to use that context for other operations, then that's the only way I can see how to test the credentials.

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

But if your only goal is to validate the credentials, then you could use DirectoryEntry directly (install System.DirectoryServices from NuGet). You'll see from the stack trace that PrincipalContext uses DirectoryEntry underneath anyway. I've found that using DirectoryEntry directly is much, much faster anyway, although it can be more complicated to work with sometimes.

以下是仅使用 DirectoryEntry 验证凭据的方法:

Here is how you would validate credentials with just 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(从 NuGet 安装 System.DirectoryServices.Protocols).这可能是验证凭据所需的最少实际网络流量.但是您可能必须弄清楚身份验证方法.默认情况下它使用 Negotiate,但如果这不起作用,您将不得不使用不同的构造函数并手动选择身份验证方法.

Another way is to use LdapConnection directly (install System.DirectoryServices.Protocols from NuGet). This is probably the least amount of actual network traffic that has to happen to validate the credentials. But you may have to figure out the authentication method. By default it uses Negotiate, but if that doesn't work, you will have to use a different constructor and choose the authentication method manually.

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

}

这篇关于如何验证传递给“PrincipalContext"的凭据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆