我将把这个问题的答案分成三个不同的部分:
- 您的方法论当前的问题
- 在线和本主题中推荐的解决方案存在的问题
- 解决方案
您的方法目前存在的问题。
NetValidatePasswordPolicy
需要其InputArgs
参数接收指向结构的指针,并且您传入的结构取决于ValidationType
你正在通过。在这种情况下,你正在通过NET_VALIDATE_PASSWORD_TYPE.NetValidateAuthentication
,这需要一个 InputArgs 为NET_VALIDATE_AUTHENTICATION_INPUT_ARG
但你传递的是一个指针NET_VALIDATE_PASSWORD_CHANGE_INPUT_ARG
.
此外,您尝试将“currentPassword”类型的值分配给NET_VALIDATE_PASSWORD_CHANGE_INPUT_ARG
结构。
然而,使用它有一个更大的根本问题。NetValidatePasswordPolicy
也就是说,您正在尝试使用此功能来验证 Active Directory 中的密码,但这不是它的用途。NetValidatePasswordPolicy
用于允许应用程序根据应用程序提供的身份验证数据库进行验证。
还有更多关于NetValidatePasswordPolicy
here https://msdn.microsoft.com/en-us/library/windows/desktop/aa370661%28v=vs.85%29.aspx.
在线和本线程中推荐解决方案的问题
网上各种文章推荐使用LogonUser
函数发现于AdvApi32.dll
但这种实现有其自身的一系列问题:
第一个是LogonUser
根据本地缓存进行验证,这意味着除非您使用“网络”模式,否则您将无法立即获得有关该帐户的准确信息。
第二个是使用LogonUser
在我看来,在 Web 应用程序上有点 hacky,因为它是为在客户端计算机上运行的桌面应用程序而设计的。但是,考虑到 Microsoft 提供的限制,如果LogonUser
给出了期望的结果,我不明白为什么不应该使用它 - 除非缓存问题。
另一个问题是LogonUser
它对您的用例的工作效果取决于您的服务器的配置方式,例如:需要在您正在验证的域上启用一些特定的权限,需要为“网络”登录设置适当的权限键入即可工作。
有关更多信息LogonUser
here https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184%28v=vs.85%29.aspx.
Also, GetLastError()
不应该使用,GetLastWin32Error()
应该使用它,因为它使用起来不安全GetLastError()
.
有关更多信息GetLastWin32Error()
here https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.getlastwin32error%28v=vs.100%29.aspx.
解决方案。
为了从 Active Directory 获取准确的错误代码,没有任何缓存问题并直接从目录服务,需要执行以下操作:当帐户出现问题时,依赖从 AD 返回的 COMException,因为最终,错误是你在寻找什么。
首先,以下是如何在对当前用户名和密码进行身份验证时从 Active Directory 触发错误:
public LdapBindAuthenticationErrors AuthenticateUser(string domain, string username, string password, string ouString)
{
// The path (ouString) should not include the user in the directory, otherwise this will always return true
DirectoryEntry entry = new DirectoryEntry(ouString, username, password);
try
{
// Bind to the native object, this forces authentication.
var obj = entry.NativeObject;
var search = new DirectorySearcher(entry) { Filter = string.Format("({0}={1})", ActiveDirectoryStringConstants.SamAccountName, username) };
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if (result != null)
{
return LdapBindAuthenticationErrors.OK;
}
}
catch (DirectoryServicesCOMException c)
{
LdapBindAuthenticationErrors ldapBindAuthenticationError = -1;
// These LDAP bind error codes are found in the "data" piece (string) of the extended error message we are evaluating, so we use regex to pull that string
if (Regex.Match(c.ExtendedErrorMessage, @" data (?<ldapBindAuthenticationError>[a-f0-9]+),").Success)
{
string errorHexadecimal = match.Groups["ldapBindAuthenticationError"].Value;
ldapBindAuthenticationError = (LdapBindAuthenticationErrors)Convert.ToInt32(errorHexadecimal , 16);
return ldapBindAuthenticationError;
}
catch (Exception e)
{
throw;
}
}
return LdapBindAuthenticationErrors.ERROR_LOGON_FAILURE;
}
这些是你的“LdapBindAuthenticationErrors”,你可以在 MSDN 中找到更多信息,here https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382%28v=vs.85%29.aspx.
internal enum LdapBindAuthenticationErrors
{
OK = 0
ERROR_INVALID_PASSWORD = 0x56,
ERROR_PASSWORD_RESTRICTION = 0x52D,
ERROR_LOGON_FAILURE = 0x52e,
ERROR_ACCOUNT_RESTRICTION = 0x52f,
ERROR_INVALID_LOGON_HOURS = 0x530,
ERROR_PASSWORD_EXPIRED = 0x532,
ERROR_ACCOUNT_DISABLED = 0x533,
ERROR_ACCOUNT_EXPIRED = 0x701,
ERROR_PASSWORD_MUST_CHANGE = 0x773,
ERROR_ACCOUNT_LOCKED_OUT = 0x775
}
然后您可以使用此枚举的返回类型并在控制器中使用它执行您需要的操作。需要注意的重要一点是,您正在查找“扩展错误消息”中的字符串“数据”部分COMException
因为这包含您正在寻找的全能错误代码。
祝你好运,我希望这会有所帮助。我测试了它,它对我来说非常有用。