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

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

问题描述

这是对我之前的问题的跟踪意见


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



背景



在我的应用程序中,我使用PrincipalContext .directoryservices.accountmanagement.principalcontext.-ctor?view = netcore-2.0#System_DirectoryServices_AccountManagement_PrincipalContext__ctor_System_DirectoryServices_AccountManagement_ContextType_System_String_System_String_System_String_ rel = nofollow noreferrer> 。我有许多集成测试,当凭据不正确(或提供的凭据不适合管理员使用)时失败,因此我希望能够抓住这一点。

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 ,但是直到第一个

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.

但是如果您的 only 目标是验证凭据,则可以使用 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天全站免登陆