gpt4 book ai didi

c# - 从 C# 调用 NetValidatePasswordPolicy 总是返回 Password Must Change

转载 作者:太空狗 更新时间:2023-10-29 20:20:18 25 4
gpt4 key购买 nike

我们有一个使用 Active Directory 对我们的用户进行身份验证的 mvc 应用程序。我们正在利用 System.DirectoryServices 并使用 PricipalContext 进行身份验证:

_principalContext.ValidateCredentials(userName, pass, ContextOptions.SimpleBind);

然而,这个方法只返回一个 bool 值,我们希望返回更好的消息,甚至将用户重定向到密码重置屏幕,例如:

  1. 用户被锁定在他们的帐户之外。
  2. 用户密码已过期。
  3. 用户需要在下次登录时更改密码。

因此,如果用户登录失败,我们将调用 NetValidatePasswordPolicy 来查看用户无法登录的原因。这似乎工作正常,但我们意识到该方法仅返回 NET_API_STATUS .NERR_PasswordMustChange 无论 Active Directory 用户的状态如何。

我发现的具有相同问题的唯一示例来自 Sublime Speech 插件 here .我使用的代码如下:

var outputPointer = IntPtr.Zero;
var inputArgs = new NET_VALIDATE_PASSWORD_CHANGE_INPUT_ARG { PasswordMatched = false, UserAccountName = username };
inputArgs.ClearPassword = Marshal.StringToBSTR(password);

var inputPointer = IntPtr.Zero;
inputPointer = Marshal.AllocHGlobal(Marshal.SizeOf(inputArgs));
Marshal.StructureToPtr(inputArgs, inputPointer, false);

using (new ComImpersonator(adImpersonatingUserName, adImpersonatingDomainName, adImpersonatingPassword))
{
var status = NetValidatePasswordPolicy(serverName, IntPtr.Zero, NET_VALIDATE_PASSWORD_TYPE.NetValidateAuthentication, inputPointer, ref outputPointer);

if (status == NET_API_STATUS.NERR_Success)
{
var outputArgs = (NET_VALIDATE_OUTPUT_ARG)Marshal.PtrToStructure(outputPointer, typeof(NET_VALIDATE_OUTPUT_ARG));
return outputArgs.ValidationStatus;
}
else
{
//fail
}
}

代码总是成功,那么为什么无论 Active Directory 用户的状态如何,outputArgs.ValidationStatus 的值每次都是相同的结果?

最佳答案

我将把这个问题的答案分成三个不同的部分:

  1. 您的方法目前存在的问题
  2. 在线和此线程中推荐解决方案的问题
  3. 解决方案

您的方法目前存在问题。

NetValidatePasswordPolicy需要它的 InputArgs参数接受一个指向结构的指针,你传入的结构取决于 ValidationType你正在传递。在这种情况下,你正在传递 NET_VALIDATE_PASSWORD_TYPE.NetValidateAuthentication ,这需要 NET_VALIDATE_AUTHENTICATION_INPUT_ARG 的 InputArgs但你传递了一个指向 NET_VALIDATE_PASSWORD_CHANGE_INPUT_ARG 的指针.

此外,您正试图将“currentPassword”类型的值分配给 NET_VALIDATE_PASSWORD_CHANGE_INPUT_ARG 结构。

但是,NetValidatePasswordPolicy 的使用存在更大的根本问题。那就是您正在尝试使用此功能来验证 Active Directory 中的密码,但这不是它的用途。 NetValidatePasswordPolicy用于允许应用程序根据应用程序提供的身份验证数据库进行验证。

关于 NetValidatePasswordPolicy 的更多信息here .

在线和本线程中推荐解决方案的问题

网上各种文章推荐使用LogonUserAdvApi32.dll 中找到的函数但是这个实现有它自己的一系列问题:

第一个是LogonUser针对本地缓存进行验证,这意味着您不会立即获得有关该帐户的准确信息,除非您使用“网络”模式。

第二个是使用LogonUser在 Web 应用程序上,在我看来有点 hacky,因为它是为在客户端计算机上运行的桌面应用程序设计的。但是,考虑到 Microsoft 提供的限制 LogonUser给出了预期的结果,我不明白为什么不应该使用它 - 除非存在缓存问题。

LogonUser 的另一个问题是它对您的用例的效果取决于您的服务器的配置方式,例如:需要在您要验证的域上启用一些特定的权限,这些权限需要到位才能进行“网络”登录打字上类。

关于 LogonUser 的更多信息here .

此外,GetLastError()不应使用,GetLastWin32Error()应该改用,因为使用 GetLastError() 不安全.

关于 GetLastWin32Error() 的更多信息here .

解决方案。

为了从 Active Directory 中获得准确的错误代码,没有任何缓存问题并直接从目录服务中获取,这是需要做的:当帐户出现问题时,依赖从 AD 返回的 COMException,因为最终, errors 就是你要找的。

首先,以下是在验证当前用户名和密码时如何从 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 .

    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
}

然后您可以使用此枚举的返回类型并在您的 Controller 中使用它执行您需要的操作。需要注意的重要一点是,您正在寻找 COMException 的“扩展错误消息”中字符串的“数据”部分。因为它包含您正在寻找的全能错误代码。

祝你好运,我希望这对你有所帮助。我对其进行了测试,它非常适合我。

关于c# - 从 C# 调用 NetValidatePasswordPolicy 总是返回 Password Must Change,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28614779/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com