Winapi PrincipalContext.ValidateCredentials的本地等效项是什么?
针对该域验证一组用户域凭据(用户名、密码、域)的本机方法是什么 换言之,我正在寻找与之相对应的本土产品:Winapi PrincipalContext.ValidateCredentials的本地等效项是什么?,winapi,active-directory,credentials,Winapi,Active Directory,Credentials,针对该域验证一组用户域凭据(用户名、密码、域)的本机方法是什么 换言之,我正在寻找与之相对应的本土产品: Boolean ValidateCredentials(String username, String password, String domain) { // create a "principal context" - e.g. your domain (could be machine, too) using(PrincipalContext pc = new Princ
Boolean ValidateCredentials(String username, String password, String domain)
{
// create a "principal context" - e.g. your domain (could be machine, too)
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain))
{
// validate the credentials
return pc.ValidateCredentials(username, password)
}
}
ValidateCredentials("iboyd", "Tr0ub4dor&3", "contoso");
这不是被问到了吗,回答到了,死了吗?
不!这个问题被问了很多。其中三次是我干的。但是你越深入研究,你就越意识到被接受的答案是错误的
微软设法在.NET中解决了这个问题,在.NET3.5中添加了PrincipalContext类。而原则性的内容并不是什么神奇的东西。在下面,它使用扁平的C风格ldap
API。但是试图对ILSpy的代码进行反向工程是行不通的。尽管有referencesource,微软仍然对.NETFramework类库的大部分源代码保密
你试过什么?
方法1:只需使用登录用户
我无法使用登录用户
。LogonUser仅在您的计算机位于您正在验证的域上(例如contoso
)或您的域信任您正在验证的域时工作。换句话说,如果有一个域控制器用于contoso.test域,那么:
LogonUser("iboyd", "contoso.test", "Tr0ub4dor&3",
LOGON32_LOGON_NETWORK, "Negotiate", ref token);
将失败并出现错误:
1326(登录失败:未知用户名或错误密码)
这是因为我指定的域不是我自己的域,也不是我信任的域
C#PrincipalContext
不会遇到此问题
方法2:只需使用SSPI(安全支持提供商接口)
SSPI是LogonUser
内部使用的。简而言之,它失败的原因与LogonUser
失败的原因相同:Windows将不信任来自不受信任域的凭据
代码相当长,提供了一个示例。psuedo代码jist为:
QuerySecurityPackageInfo("Negotiate");
// Prepare client message (negotiate)
AcquireCredentialsHandle(....); //for the client
InitializeSecurityContext(...); //on the returned client handle
CompleteAuthToken(...); //on the client context
// Prepare server message (challenge).
AcquireCredentialsHandle(...); //for the server
AcceptSecurityContext(...); //on the returned server handle
CompleteAuthToken(...); //on the server context
// Prepare client message (authenticate).
AcquireCredentialsHandle(....); //for the client
InitializeSecurityContext(...); //on the returned client handle
CompleteAuthToken(...); //on the client context
// Prepare server message (authentication).
AcquireCredentialsHandle(...); //for the server
AcceptSecurityContext(...); //on the returned server handle
CompleteAuthToken(...); //on the server context
如果您的计算机加入到您正在验证凭据的域,则此代码非常有效。但是,一旦您尝试验证来自外部域的一组域Credentail,它就会失败
C#PrincipalContext
不会遇到此问题
方法3:只需使用LDAP
有些人可能建议使用
这是一种误导,因为AdsGetObject
不支持传递用户名/密码:
HRESULT ADsGetObject(
_In_ LPCWSTR lpszPathName,
_In_ REFIID riid,
_Out_ VOID **ppObject
);
相反,您只需询问用户的情况
也许您的意思是adopenobject
:
HRESULT ADsOpenObject(
_In_ LPCWSTR lpszPathName,
_In_ LPCWSTR lpszUserName,
_In_ LPCWSTR lpszPassword,
_In_ DWORD dwReserved,
_In_ REFIID riid,
_Out_ VOID **ppObject
);
您可以在其中指定要连接的凭据
C#PrincipalContext
不会遇到此问题
方法4:只需使用AdsOpenObject
有些人可能建议使用:
撇开我构造的路径无效这一事实不谈,撇开当您只知道以下情况时无法构造有效的LDAP路径这一事实不谈:
- {username}
- {密码}
- {域}
{username}
,DC={domain}
不是任何用户的有效LDAP路径
尽管存在LDAP路径难题,但基本问题是尝试查询LDAP。这是错误的
我们希望验证用户的广告凭证
每当我们尝试查询LDAP服务器时,如果用户没有查询LDAP的权限,即使他们的凭据有效,我们也会失败
当您将凭据传递给AdsOpenObject
时,它们用于指定您要连接的用户。连接后,将对LDAP执行查询。当您没有查询LDAP的权限时,AdsOpenObject将失败
更令人恼火的是,即使您确实有权在LDAP中查询用户,您仍然不必要地执行LDAP查询—这是一项昂贵的操作
C#PrincipalContext
不会遇到此问题
方法5:将ADO与ADsDSOObject提供程序一起使用
有许多人只是将ADsDSOObject OLEDB提供程序与ADO一起使用来查询LDAP。这就解决了必须找到正确的LDAP路径的问题—您不必知道用户的LDAP路径
String sql =
'SELECT userAccountControl FROM "LDAP://DC=contoso,DC=test"
'WHERE objectClass="user"
'and sAMAccountName = "iboyd"';
String connectionString = "Provider=ADsDSOObject;Password=Tr0ub4dor&3;
User ID=iboyd;Encrypt Password=True;Mode=Read;
Bind Flags=0;ADSI Flag=-2147483648";
Connection conn = new ADODB.Connection();
conn.ConnectionString = connectionString;
conn.Open();
IRecordset rs = conn.Execute(sql);
这是可行的;它解决了不知道用户LDAP路径的问题。但它不能解决如果你没有查询广告的权限,那么它就会失败的问题
另外还有一个问题,即它在查询Active Directory时应该验证凭据
C#PrincipalContext
不会遇到此问题
方法6:只需使用PrincipalContext.ValidateCredentials即可
.NET 3.5类PrincipalContext
提供了一个仅允许您在知道以下信息的情况下验证凭据的功能:
- 用户名
- 密码
- 域名
ValidateCredentials
CredentialValidator.Validate
BindLdap
new LdapDirectoryIdentifier
new LdapConnection
ldapConnection.SessionOptions.FastConcurrentBind();
lockedLdapBind
Bind
周围有一大堆大概很重要的代码。依赖项注入有很多起伏,函数太少——这些都是您在过于复杂的代码结构中遇到的正常困难。复杂性与编程的SSPI相当。没有人理解SSPI代码,我已经编写了调用它的代码
注意:此问题不询问如何验证本地凭据,而不是验证本地凭据。它也没有问如何两者兼顾。在本例中,我只是问如何做在.NET世界中已经可用但在本机世界中可用的事情
不幸的是:
System.Security.DirectoryServices.AccountManagement.PrincipalContext
未通过COM可调用包装器公开:
现在我已经花了两个半小时
ValidateCredentials
CredentialValidator.Validate
BindLdap
new LdapDirectoryIdentifier
new LdapConnection
ldapConnection.SessionOptions.FastConcurrentBind();
lockedLdapBind
Bind
System.Security.DirectoryServices.AccountManagement.PrincipalContext