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

C# 从ASP.NET应用程序查询Active Directory对象属性将返回旧结果,c#,c++,asp.net,active-directory,adsi,C#,C++,Asp.net,Active Directory,Adsi,好几天来,我一直在尝试使用一些自定义的基于Active Directory的身份验证。这一切在理论上都是可行的,但显然我的理论是错误的。登录到域的用户在登录到ASP.NET应用程序时,会将字符串标记(例如PIN码)写入Active Directory中他们自己的属性字段(不管是哪一个,但我使用了它),此PIN始终以编程方式生成和写入 >大致解释一下,Web浏览器加载java applet,然后加载C++中编写的本地DLL,它生成并写入当前用户的ActiveDirectory域中的PIN。该DL

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

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

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

        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应用程序,我将使用方法执行它。我将通过进程退出代码返回我的PIN码(因为它无论如何都是整数)。应用程序使用类似的C++代码作为DLL。p> 因此,我启动我的应用程序,等待它完成,读取退出代码。返回错误的值

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


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

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

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

        var ctx = new DirectoryContext(
            DirectoryContextType.DirectoryServer,
            ipAddress,
            userName, // in the form DOMAIN\UserName 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());
                    }
                }
            }
        }
var ctx=new DirectoryContext(
DirectoryContextType.DirectoryServer,
IP地址,
用户名,//格式为DOMAIN\userName,否则远程目录服务器将失败
密码);
var domain=domain.GetDomain(ctx);
var值=新列表();
foreach(域中的DomainController dc.DomainController)
{
使用(var条目)=
新目录条目(
“LDAP://”+dc.IPAddress,
用户名,
密码)
{
使用(var search=newdirectorysearcher(条目))
{
search.Filter=“(&(primaryinternationalsdnnumber=*)(sAMaccountName=“+userName+”)”;
var result=search.FindOne();
var de=result.GetDirectoryEntry();
if(de.Properties[“primaryinternationalsdnnumber”].Value!=null)
{
Add(de.Properties[“primaryinternationalsdnnumber”].Value.ToString());
}
}
}
}

如果我是你,我会先看看你的active directory复制。我假设这两个测试应用程序运行在同一台计算机上,因此它们可能都使用同一个域控制器。IIS服务器和客户端不会随机更新值,因此,必须跨域控制器复制新值,这需要时间,具体取决于您的基础结构。@Ashigore即使在不同的计算机上运行,这两个测试应用程序也能工作。此应用程序尚未部署(在IIS Express中运行)。@Ashigore此外,我还创建了一个测试ASP.NET应用程序,该应用程序仅显示AD中的值,在使用控制台应用程序编写之后,或手动执行,都无所谓。。。同样的问题。