问题描述
这是对我之前的问题的跟进.
验证传递给 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
,但是直到第一次使用 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"的凭据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!