从 ASP.NET 应用程序 returns 旧结果中查询 Active Directory 对象 属性
Querying an Active Directory object property from ASP.NET application returns old results
几天来,我一直在尝试让一些基于 Active Directory 的自定义身份验证工作。这一切都在理论上有效,但显然我的理论是错误的。登录到域的用户将字符串标记(例如 PIN 码)写入 Active Directory 中他们自己的 属性 字段(哪个并不重要,但我为此使用了 primaryInternationISDNNumber)登录 ASP.NET 应用程序 此 PIN 始终以编程方式生成和写入。
粗略地解释一下,Web 浏览器加载一个 Java 小程序,然后加载一个用 C++ 编写的本机 DLL,它生成 PIN 并将其写入当前用户的 Active Directory 字段。然后,该 DLL return 将生成的 PIN 发送到小程序,然后小程序将其传递给浏览器,浏览器使用 returned 数据执行 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 应用程序使用 Platform Invoke 加载。此 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();
令我非常意外的是,这段代码在正常工作了一天之后,开始 returning 以前的值,就像之前的托管代码一样!
所以现在我想,好吧,我无法逃脱 IIS 应用程序池,因为这一定是 IIS 应用程序池的问题,我将创建一个我将执行的本机 Windows 应用程序通过使用 Process.Start 方法。我将通过进程退出代码 return 我的 PIN 码(因为它无论如何都是一个整数)。该应用程序使用与上述 DLL 类似的 C++ 代码。
所以我启动我的应用程序,等待它完成,然后读取退出代码。 Returns 错误的价值!
但是好吧,我要说的是,该进程是使用当前用户凭据启动的,这又是 IIS 应用程序池。所以我在不同的凭据下启动应用程序。你猜怎么着......它 return 又是旧值 (?!?!?!)。
我认为 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());
}
}
}
}
几天来,我一直在尝试让一些基于 Active Directory 的自定义身份验证工作。这一切都在理论上有效,但显然我的理论是错误的。登录到域的用户将字符串标记(例如 PIN 码)写入 Active Directory 中他们自己的 属性 字段(哪个并不重要,但我为此使用了 primaryInternationISDNNumber)登录 ASP.NET 应用程序 此 PIN 始终以编程方式生成和写入。
粗略地解释一下,Web 浏览器加载一个 Java 小程序,然后加载一个用 C++ 编写的本机 DLL,它生成 PIN 并将其写入当前用户的 Active Directory 字段。然后,该 DLL return 将生成的 PIN 发送到小程序,然后小程序将其传递给浏览器,浏览器使用 returned 数据执行 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 应用程序使用 Platform Invoke 加载。此 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();
令我非常意外的是,这段代码在正常工作了一天之后,开始 returning 以前的值,就像之前的托管代码一样!
所以现在我想,好吧,我无法逃脱 IIS 应用程序池,因为这一定是 IIS 应用程序池的问题,我将创建一个我将执行的本机 Windows 应用程序通过使用 Process.Start 方法。我将通过进程退出代码 return 我的 PIN 码(因为它无论如何都是一个整数)。该应用程序使用与上述 DLL 类似的 C++ 代码。
所以我启动我的应用程序,等待它完成,然后读取退出代码。 Returns 错误的价值!
但是好吧,我要说的是,该进程是使用当前用户凭据启动的,这又是 IIS 应用程序池。所以我在不同的凭据下启动应用程序。你猜怎么着......它 return 又是旧值 (?!?!?!)。
我认为 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());
}
}
}
}