我有一个ASP.NET Web API服务,它在启用了Windows身份验证的Web服务器上运行。
我有一个基于MVC4构建的客户端站点,它运行在使用HttpClient从服务中提取数据的同一Web服务器上的不同站点中。此客户端站点在启用身份模拟的情况下运行,并且还使用Windows身份验证。
Web服务器是带有IIS 7.5的Windows Server 2008 R2。
我遇到的挑战是让HttpClient通过当前的Windows用户作为其身份验证过程的一部分。我已经以这种方式配置了HttpClient:
var clientHandler = new HttpClientHandler();
clientHandler.UseDefaultCredentials = true;
clientHandler.PreAuthenticate = true;
clientHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
var httpClient = new HttpClient(clientHandler);
我的理解是,在启用了身份模拟的情况下运行网站,然后以这种方式构建客户端应该导致客户端使用当前登录用户的模拟身份对服务进行身份验证。
这没有发生。事实上,客户端似乎根本没有进行身份验证。
该服务配置为使用Windows身份验证,这似乎完美。我可以在我的网络浏览器中转到http://server/api/shippers并提示进行Windows身份验证,输入后我会收到请求的数据。
在IIS日志中,我看到收到的API请求没有进行身份验证并收到401质询响应。
关于这个的文档似乎很少。
我需要深入了解可能出现的问题或使用此应用程序使用Windows身份验证的其他方法。
谢谢你, 克雷格
答案 0 :(得分:32)
我已经调查了HttpClientHandler的源代码(我能够掌握的最新版本),这可以在SendAsync方法中找到:
// BeginGetResponse/BeginGetRequestStream have a lot of setup work to do before becoming async
// (proxy, dns, connection pooling, etc). Run these on a separate thread.
// Do not provide a cancellation token; if this helper task could be canceled before starting then
// nobody would complete the tcs.
Task.Factory.StartNew(startRequest, state);
现在,如果你在代码中检查SecurityContext.IsWindowsIdentityFlowSuppressed()的值,你很可能会得到真的。结果,StartRequest方法在新线程中使用asp.net进程的凭据(而不是模拟用户的凭据)执行。
有两种可能的方法。如果您有权访问您的服务器aspnet_config.config,您应该设置以下设置(在web.config中设置这些设置似乎没有效果):
<legacyImpersonationPolicy enabled="false"/>
<alwaysFlowImpersonationPolicy enabled="true"/>
如果您无法更改aspnet_config.config,则必须创建自己的HttpClientHandler以支持此方案。
关于使用FQDN的更新
此处遇到的问题是Windows中的一项功能,旨在防止“反射攻击”。要解决此问题,您需要在尝试访问服务器的计算机上将您尝试访问的域列入白名单。请按照以下步骤操作:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0
注册表项。BackConnectionHostNames
( ENTER )。您可以阅读有关问题here的完整知识库文章。
答案 1 :(得分:9)
我也有同样的问题。感谢@tpeczek所做的研究,我开发了以下解决方案:我使用的WebClient类在同一个线程上发出请求,而不是使用HttpClient(创建线程并发送请求异步)。这样做使我能够将用户的身份从另一个ASP.NET应用程序传递给WebAPI。
明显的缺点是这不会起作用。
var wi = (WindowsIdentity)HttpContext.User.Identity;
var wic = wi.Impersonate();
try
{
var data = JsonConvert.SerializeObject(new
{
Property1 = 1,
Property2 = "blah"
});
using (var client = new WebClient { UseDefaultCredentials = true })
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json; charset=utf-8");
client.UploadData("http://url/api/controller", "POST", Encoding.UTF8.GetBytes(data));
}
}
catch (Exception exc)
{
// handle exception
}
finally
{
wic.Undo();
}
注意:需要NuGet包:Newtonsoft.Json,它与WebAPI使用的JSON序列化程序相同。
答案 2 :(得分:0)
这不起作用的原因是因为您需要双跳认证。
第一跳是Web服务器,使用Windows身份验证进行模拟工作没有问题。但是,当使用HttpClient或WebClient将您验证到另一台服务器时,Web服务器需要在有权执行必要委派的帐户上运行。
有关详细信息,请参阅以下内容:
http://blogs.technet.com/b/askds/archive/2008/06/13/understanding-kerberos-double-hop.aspx
使用“setspn”命令修复:
http://www.phishthis.com/2009/10/24/how-to-configure-ad-sql-and-iis-for-two-hop-kerberos-authentication-2/
(您将需要足够的访问权限来执行这些操作。)
请考虑如果允许任何服务器转发您的凭据会发生什么情况......为了避免此安全问题,域控制器需要知道允许哪些帐户执行委派。
答案 3 :(得分:0)
要模拟原始(经过身份验证的)用户,请在Web.config文件中使用以下配置:
<authentication mode="Windows" />
<identity impersonate="true" />
使用此配置,ASP.NET始终模拟经过身份验证的用户,并使用经过身份验证的用户的安全上下文执行所有资源访问。