如何验证传递给“ PrincipalContext”的凭据 [英] How to validate the credentials passed to a `PrincipalContext`
问题描述
这是对我之前的问题的跟踪意见
验证传递给的凭据的正确方法是什么
PrincipalContext
?
背景
在我的应用程序中,我使用 。我有许多集成测试,当凭据不正确(或提供的凭据不适合管理员使用)时失败,因此我希望能够抓住这一点。
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屋!