无法使用HttpClient对ASP.NET Web Api服务进行身份验证

Cra*_*son 37 windows-authentication iis-7.5 wcf-web-api asp.net-mvc-4 asp.net-web-api

我有一个ASP.NET Web API服务,它在启用了Windows身份验证的Web服务器上运行.

我有一个基于MVC4构建的客户端站点,它运行在同一Web服务器上的不同站点上,该站点使用HttpClient从服务中提取数据.此客户端站点在启用身份模拟的情况下运行,并且还使用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);
Run Code Online (Sandbox Code Playgroud)

我的理解是,在启用了身份模拟的情况下运行站点,然后以这种方式构建客户端应该导致客户端使用当前登录用户的模拟身份对服务进行身份验证.

这不会发生.事实上,客户端似乎根本没有进行身份验证.

该服务配置为使用Windows身份验证,这似乎完美.我可以在我的网络浏览器中访问http:// server/api/shippers并提示进行Windows身份验证,输入后我会收到请求的数据.

在IIS日志中,我看到收到的API请求没有进行身份验证并收到401质询响应.

关于这个的文档似乎很少.

我需要深入了解可能出现的问题或使用此应用程序使用Windows身份验证的其他方法.

谢谢,克雷格

tpe*_*zek 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);
Run Code Online (Sandbox Code Playgroud)

现在,如果您在代码中检查SecurityContext.IsWindowsIdentityFlowSuppressed()的值,您很可能会得到真实.结果,StartRequest方法在新线程中使用asp.net进程的凭据(而不是模拟用户的凭据)执行.

有两种可能的方法.如果您有权访问您的服务器aspnet_config.config,则应设置以下设置(在web.config中设置这些设置似乎没有效果):

<legacyImpersonationPolicy enabled="false"/>
<alwaysFlowImpersonationPolicy enabled="true"/>
Run Code Online (Sandbox Code Playgroud)

如果您无法更改aspnet_config.config,则必须创建自己的HttpClientHandler以支持此方案.

关于FQDN使用的更新

您在此处遇到的问题是Windows中的一项功能,旨在防止"反射攻击".要解决此问题,您需要在尝试访问服务器的计算机上将您尝试访问的域列入白名单.请遵循以下步骤:

  1. 转到开始 - >运行 - > regedit
  2. 找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0注册表项.
  3. 右键单击它,选择" 新建",然后选择" 多字符串值".
  4. 键入BackConnectionHostNames(ENTER).
  5. 右键单击刚创建的值,然后选择" 修改".
  6. 在值框中输入本地计算机上的站点的主机名,然后单击" 确定"(每个主机名/ FQDN需要在其自己的行上,没有通配符,名称必须完全匹配) .
  7. 保存所有内容并重启机器

您可以在此处阅读有关此问题的完整知识库文章.


Jos*_*hua 10

我也有同样的问题.感谢@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();
}
Run Code Online (Sandbox Code Playgroud)

注意:需要NuGet包:Newtonsoft.Json,它与WebAPI使用的JSON序列化程序相同.