从ASP.NET应用程序查询Active Directory对象属性将返回旧结果

Querying an Active Directory object property from ASP.NET application returns old results

本文关键字:属性 返回 结果 对象 Directory NET ASP 应用程序 查询 Active      更新时间:2023-10-16

几天来,我一直在尝试使用一些基于Active Directory的自定义身份验证。这一切都在理论上可行,但显然我的理论是错误的。登录到域的用户在登录到ASP.NET应用程序时,会在Active Directory中的自己的属性字段中写入字符串令牌(例如PIN码)(哪一个并不重要,但我使用了primaryInternalISDNNumber)。此PIN始终以编程方式生成和写入。

大致解释一下,web浏览器加载一个Java小程序,然后加载一个用C++编写的本地DLL,该DLL生成PIN并将其写入当前用户的Active Directory字段。然后,该DLL将生成的PIN返回给小程序,小程序将其传递给浏览器,浏览器使用返回的数据执行AJAX调用以启动身份验证。有权访问AD的应用程序读取连接用户对象的此字段值,并检查它是否与用户提供的匹配。如果PIN码匹配,则用户已成功通过身份验证。

这是ASP.NET应用程序用于读取AD:的示例代码

        using (var de = new DirectoryEntry("LDAP://" + domainName))
        {
            using (var adSearch = new DirectorySearcher(de))
            {
                // Get user from active directory.
                adSearch.Filter = "(sAMAccountName=" + userName.Trim().ToLower(CultureInfo.CurrentCulture) + ")";
                var adSearchResult = adSearch.FindOne();
                var entry = adSearchResult.GetDirectoryEntry();
                var pinCodeProp = entry.Properties["primaryInternationISDNNumber"];
                return pinCodeProp != null ? pinCodeProp.Value : string.Empty;
            }
        }

这通常效果很好。但是通常是不可接受的。它需要始终有效。

问题在于,ASP.NET应用程序有时会获取以前在该字段中的值,而不是实际值。好像有某种缓存。我尝试添加de.UsePropertyCache = false,但结果相同。

出于测试目的,我创建了两个Win32控制台应用程序。一个写PIN码,另一个读PIN码。他们总是工作得很好!

我想,这一定是IIS应用程序池的问题。因此,我创建了一个本机DLL,它由ASP.NET应用程序使用平台调用加载。这个DLL创建一个新线程,调用CoInitialize并读取PIN码。这是代码:

    pszFqdn = argv[1];
    pszUserName = argv[2];
    pszPassword = argv[3];
    IADs *pObject = NULL;
    HRESULT hr = S_OK;
    hr = CoInitialize(NULL);
    if (SUCCEEDED(hr))
    {
        hr = ADsOpenObject(pszFqdn, pszUserName, pszPassword, ADS_SECURE_AUTHENTICATION, IID_IADs, (LPVOID*)&pObject);
        if (SUCCEEDED(hr) && pObject)
        {
            VARIANT var;
            VariantInit(&var);
            hr = pObject->Get(CComBSTR("primaryInternationalISDNNumber"), &var);
            if ((SUCCEEDED(hr) && var.bstrVal) || hr == 0x8000500d)
            {
                if (hr != 0x8000500d)
                {
                    // convert the BSTR received to TCHAR array
                    std::wstring wsValue(var.bstrVal, SysStringLen(var.bstrVal));
                    // copy the received value to somewhere
                    // ... not relevant
                }
                VariantClear(&var);
            }
            pObject->Release();
        }
    }
    CoUninitialize();

令我惊讶的是,这段代码在正常工作了一天后,开始返回以前的值,就像以前的托管代码一样!

所以现在我想,好吧,我无法逃离IIS应用程序池,由于这一定是IIS应用程序库的问题,我将创建一个本机Windows应用程序,我将使用Process.Start方法执行该应用程序。我将通过进程退出代码返回我的PIN码(因为它无论如何都是整数)。该应用程序使用与上述DLL类似的C++代码。

所以我启动我的应用程序,等待它完成,读取退出代码。返回错误的值!

但是,好吧,我想说,这个过程是使用当前的用户凭据启动的,这也是IIS应用程序池。因此,我使用不同的凭据启动应用程序。你猜怎么着。。。。。它再次返回旧值(?!?!?)。

我还以为Java是地狱。。。有人知道这里可能发生了什么吗?

这确实是复制。由于我不想在读取字段之前强制复制(无论如何,这可能是一项耗时的操作),我想出了一个主意,从每个域控制器读取此字段,并检查其中是否有字段与提供的值匹配。

由于它可能会对某些人有所帮助,我使用以下代码做到了这一点。

        var ctx = new DirectoryContext(
            DirectoryContextType.DirectoryServer,
            ipAddress,
            userName, // in the form DOMAINUserName or else it would fail for a remote directory server
            password);
        var domain = Domain.GetDomain(ctx);
        var values = new List<string>();
        foreach (DomainController dc in domain.DomainControllers)
        {
            using (var entry =
                    new DirectoryEntry(
                        "LDAP://" + dc.IPAddress,
                        userName,
                        password))
            {
                using (var search = new DirectorySearcher(entry))
                {
                    search.Filter = "(&(primaryInternationalISDNNumber=*)(sAMaccountName=" + userName + "))";
                    var result = search.FindOne();
                    var de = result.GetDirectoryEntry();
                    if (de.Properties["primaryInternationalISDNNumber"].Value != null)
                    {
                        values.Add(de.Properties["primaryInternationalISDNNumber"].Value.ToString());
                    }
                }
            }
        }