从 C# 调用 NetValidatePasswordPolicy 始终返回密码必须更改

2024-03-09

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

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

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

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

因此,如果用户登录失败,我们会调用NetValidatePasswordPolicy看看为什么用户无法登录。这似乎工作得很好,但我们意识到这个方法只是返回NET_API_STATUS.NERR_PasswordMustChange无论 Active Directory 用户的状态如何。

我发现的具有相同问题的唯一示例来自 Sublime Speech 插件here https://github.com/Mozillion/SublimeSpeech/blob/master/lib/pywin32bit/win32/Demos/NetValidatePasswordPolicy.py。我使用的代码如下:

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
    }   
}

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


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

  1. 您的方法论当前的问题
  2. 在线和本主题中推荐的解决方案存在的问题
  3. 解决方案

您的方法目前存在的问题。

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因为这包含您正在寻找的全能错误代码。

祝你好运,我希望这会有所帮助。我测试了它,它对我来说非常有用。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从 C# 调用 NetValidatePasswordPolicy 始终返回密码必须更改 的相关文章

随机推荐

  • Font-Face 使用绝对路径

    是否可以使用绝对路径 fontace 我有以下目录结构 HP font Monoton woff Monoton eot css font css fontface css html index html 定义字体 font face fo
  • 在 C++11 中使用零或 NULL 哪个更好?

    如今 有了C 11 推荐使用什么 Zero or NULL 第一个还是第二个如果 int p getPointer if 0 p something if NULL p something UPDATE 我忘记新的了 if nullptr
  • Entity Framework Core - 版本 5+ 与输出数据库无关

    我有一个使用 EF Core 的应用程序 最初是使用 V3 1 1 编写的 我们决定冒险更新到 v5 0 1 现在它已经不再预览了 该应用程序支持 MSSQL 和 SQLite 最初运行得很好 迁移的创建基本上与工具和文档无关here ht
  • 您的启动项目“项目”未引用 Microsoft.EntityFrameworkCore.Design

    我正在使用 EntityFrameworkCore 版本 2 2 2 构建 WPF 应用程序 然而 当我想要Add Migration它向我输出一个错误 Your startup project project doesn t refere
  • 如何配置星号即时消息

    星号支持即时消息吗 我尝试为 IM 配置星号 从这个例子 https groups google com forum msg csipsimple users stBJYGQ88fw OgLW8CLVvIkJ 但是当我尝试将 IM 发送到另
  • 下限 > 0 的 .Net 数组

    虽然这可能是一件奇怪的事情 但我需要在 Net 中创建一个下限 gt 0 的数组 乍一看 这似乎是可能的 使用 Array CreateInstance typeof Object new int 2 new int 9 产生所需的结果 下
  • 一个maven项目如何依赖另一个本地maven项目?

    我有两个 Maven 项目project foo pom xml and project bar pom xml 我有foo取决于bar and I want that every timefoo pom xmlcompiles it ll
  • 单元测试模拟 GCS

    我很难找到一种方法来进行单元测试read and write此类中存在的方法 我正在尝试使用模拟补丁库创建一个模拟 以避免调用 Google Storage 但我很难弄清楚如何做到这一点 from google cloud import s
  • 在c#中用0-9之间的随机数填充数组[重复]

    这个问题在这里已经有答案了 可能的重复 在 C 中用 0 9 之间的唯一随机数填充数组 https stackoverflow com questions 10688044 filling a array with uniqe random
  • 使用相关字段更新项目的示例过滤器挂钩

    我有 3 个集合 客户 经销商 和 订单 客户与经销商之间存在多对一的关系 经销商与客户之间是一对多的关系 订单与客户之间存在多对一的关系 我想使用先前配置的多对一关系根据所选客户自动设置orders reseller 字段 payload
  • img 元素上的 HTML 宽度/高度属性和 CSS 宽度/高度属性有什么区别?

    超文本标记语言 img 元素可以有width height属性 也可以有CSSwidth height特性 img src xxx img width 16 height 16 style width 16px height 16px HT
  • 我无法从数据库中检索图像

    我有一个包含电影名称 描述和封面图片的数据库 封面图片字段类型为 blob 问题是我无法从数据库中检索它 我想在他们旁边的封面图片上显示电影名称 如何做到这一点 这是我的代码 table border 1 width 100 tr td t
  • Perl 中的“字符串化”是什么?

    在 CPAN 模块的文档中DateTime http search cpan org drolsky DateTime 0 66 lib DateTime pm Formatters And Stringification我发现了以下内容
  • 在 Swift 中子类化 UINavigationBar

    我正在尝试创建一个自定义UINavigationBar类 然后使用Storyboard将其设置为我的班级UINavigationController的导航栏 这是我的代码UINavigationBar class class CustomN
  • 使用外部服务的 Shopify 自定义搜索

    我的情况 我们有一个 Shopify 商店 我们还有一个在 AWS 上实施和托管的搜索引擎 我们需要使用该搜索引擎而不是 Shopify 商店中的默认 search 理想情况下 当用户尝试搜索某些内容时 他们的查询 以及可能的其他内容 例如
  • C++ 静态变量总是在主线程上被破坏吗?

    简短的问题 C 11 静态 非线程本地 变量是否总是在主线程上分散注意力 它们是否总是仅在程序退出时被销毁 考虑到我们不手动调用它们的析构函数 UPDATE 为了简洁起见 我们假设调用了析构函数 我们没有拔掉插头 我们没有杀死 9 全局对象
  • 使用“Runtime.getRuntime().exec”从 Java 启动的进程无法访问文件系统位置

    从 Java 启动的进程Runtime getRuntime exec权限较少 我在 JavaDocs 中没有看到对此的描述以及如何管理它 如何保证启动的进程具有相同的环境和权限 更多细节 编写轻量级 Eclipse 插件maven htt
  • 安全 Rust 中的转换会导致运行时错误吗?

    我正在摆弄一点Any和铸造只是为了更深入地了解 Rust 从 C 开始 我已经习惯了强制转换可能导致运行时异常 因为在 C 中强制转换基本上意味着亲爱的编译器 相信我 我知道我在做什么 请将其转换为int32因为我知道它会起作用 但是 如果
  • R中的正态分布图[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在尝试使用plot命令在r中绘制正态分布 plot x dnorm x mean sd 使用我的平均值和标准差值 然而 使用这些值 y
  • 从 C# 调用 NetValidatePasswordPolicy 始终返回密码必须更改

    我们有一个使用 Active Directory 来验证用户身份的 mvc 应用程序 我们正在利用System DirectoryServices并使用PricipalContext验证 principalContext ValidateC