如何验证传递给 `PrincipalContext` 的凭据
How to validate the credentials passed to a `PrincipalContext`
这是 的跟进。
问题
What is the correct way of validating the credentials passed to a PrincipalContext
?
背景
在我的应用程序中,我使用 PrincipalContext(ContextType, String, String, String)
实例化了一个 PrincipalContext
。我有许多集成测试在凭据不正确(或提供的凭据不适用于管理员)时失败,所以我希望能够捕捉到这一点。
如果凭证无效 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
分配给局部变量并捕获异常吗?
您可以将上下文的实际使用移动到 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
(从 NuGet 安装 System.DirectoryServices
)。您将从堆栈跟踪中看到 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
(从 NuGet 安装 System.DirectoryServices.Protocols
)。这可能是验证凭据所需的最少实际网络流量。但是您可能必须弄清楚身份验证方法。默认情况下它使用 Negotiate,但如果这不起作用,您将不得不使用不同的构造函数并手动选择身份验证方法。
var id = new LdapDirectoryIdentifier("domain.local");
var conn = new LdapConnection(id, new NetworkCredential("username", "password"));
try {
conn.Bind();
} catch (Exception e) {
}
这是
问题
What is the correct way of validating the credentials passed to a
PrincipalContext
?
背景
在我的应用程序中,我使用 PrincipalContext(ContextType, String, String, String)
实例化了一个 PrincipalContext
。我有许多集成测试在凭据不正确(或提供的凭据不适用于管理员)时失败,所以我希望能够捕捉到这一点。
如果凭证无效 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
分配给局部变量并捕获异常吗?
您可以将上下文的实际使用移动到 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
(从 NuGet 安装 System.DirectoryServices
)。您将从堆栈跟踪中看到 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
(从 NuGet 安装 System.DirectoryServices.Protocols
)。这可能是验证凭据所需的最少实际网络流量。但是您可能必须弄清楚身份验证方法。默认情况下它使用 Negotiate,但如果这不起作用,您将不得不使用不同的构造函数并手动选择身份验证方法。
var id = new LdapDirectoryIdentifier("domain.local");
var conn = new LdapConnection(id, new NetworkCredential("username", "password"));
try {
conn.Bind();
} catch (Exception e) {
}