我有一个带有windows身份验证的.net/c#web应用程序(web api)。该服务托管在我的本地计算机IIS 10上。应用程序池标识设置为我,当前登录的windows用户。计算机在active directory域中。
我想使用当前登录到应用程序的帐户访问共享文件。文件具有适当的权限。为此,我使用类似的模拟:
if (HttpContext.Current.User.Identity is WindowsIdentity windowsIdentity)
{
using (windowsIdentity.Impersonate())
{
FileStream stream = new FileStream(@"\serversharefile.ext", FileMode.Open, FileAccess.Read);
}
}
我使用当前windows帐户登录,与应用程序池中设置的身份相同。这与托管应用程序的本地计算机上的共享文件配合使用很好。但无法使用位于另一台计算机上的远程共享文件。另一台计算机也在active directory域中。
从主机上,我可以使用windows资源管理器或浏览器访问共享文件。此外,如果我不模拟用户,.net尝试使用应用程序池标识帐户(设置为同一用户,我)访问共享文件,并且本地和远程文件都成功了。
它也适用于从advapi32.dll的LogoUser方法获得的模拟身份。但它需要用户密码,我不想向已经登录到应用程序的用户请求密码。
我做错了什么?
更新:如果共享文件位于主机上,则由windows生成的登录事件(事件查看器中的安全选项卡)显示正确的用户。如果共享文件位于另一台计算机上,则此计算机上的windows生成的登录事件将显示匿名用户。所以,账户不知怎么就丢了。
更新2:如果我在IIS上运行像localhost(url中的localhost)这样的站点,模拟就会起作用。但如果我使用ip或站点名称运行它,它就会停止工作。
更新3:Wireshark显示获取委派票证(访问共享文件服务器)的请求失败,错误为"KRB5KDC_ERR_BADOPTION NT状态:Status_NOT_FOUND"。AD中允许应用程序池用户的委派。在cmd中执行Dir命令时,可以成功检索到没有委派的同一票证(对于cifs/fileshareservername)(wireshark显示)。似乎是广告中的问题。
不能确定你所做的是否是错误的,但我可以告诉你我做了什么来做一件非常相似的事情。我的.Net网站通常没有WindowsLogin,所以我不得不进行额外的跳转,我认为你可以这样做来促进同样的事情,只是可能不是最好的答案。
登录时(在我的membershipProvider中),我运行以下代码:
try
{
if (LogonUser(user,domain,password, [AD_LOGIN],
LOGON32_PROVIDER_DEFAULT, ref handle))
{
IntPtr tokenDuplicate = IntPtr.Zero;
if (DuplicateToken(handle, SecurityImpersonation,
ref tokenDuplicate) != 0)
{
// store off duplicate token here
}
}
}
finally
{
if (handle != IntPtr.Zero)
{
CloseHandle(handle);
}
}
然后当你需要模拟时,这样做:
var context = WindowsIdentity.Impersonate(tokenDuplicate);
try
{
// do your file access here
}
finally
{
context.Dispose();
}
我不得不对这个tokenDuplicate变量进行一些有趣的转换。它是一个整数值,但指向存储令牌信息的特定内存地址。只要您登录,它就会保持良好状态。
为什么你不能直接在你的身份上进行模拟,不知道。我只知道它对我使用令牌有效,这是我获得可以用于模拟的令牌的方法。
我开始使用以下设置。
IIS:
- 应用程序池标识设置为特定用户(比如IISUser)
- 已为IIS站点启用Windows身份验证。内核模式已启用(重要!)
所有其他魔术都发生在Active directory中:
- 具有共享文件的计算机具有SPN:cifs/%Computer_name%
- 托管计算机(安装IIS的位置)受信任进行委派。委派选项卡->仅信任此计算机委派到指定的服务->使用任何身份验证协议。然后从项目1中选择SPN。重要提示:您应该选择计算机SPN,而不是IISUser SPN
- IISUser对于来自项目1的SPN的委派是受信任的