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

Posted

技术标签:

【中文标题】无法使用 HttpClient 对 ASP.NET Web Api 服务进行身份验证【英文标题】:Unable to authenticate to ASP.NET Web Api service with HttpClient 【发布时间】:2012-05-05 17:46:32 【问题描述】:

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

我有一个基于 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 身份验证可能出现的问题或其他方式。

谢谢你, 克雷格

【问题讨论】:

【参考方案1】:

我已经研究了 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 中旨在防止“反射攻击”的功能。要解决此问题,您需要在尝试访问服务器的计算机上将您尝试访问的域列入白名单。请按照以下步骤操作:

    转到开始 --> 运行 --> regedit 找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0 注册表项。 右键单击它,选择New,然后选择Multi-String Value。 输入BackConnectionHostNamesENTER)。 右键单击刚刚创建的值并选择修改。 将本地计算机上的站点的主机名放入值框中,然后单击确定(每个主机名/FQDN 需要在其自己的行中,没有通配符,名称必须完全匹配)。 保存所有内容并重新启动机器

您可以阅读有关问题here 的完整知识库文章。

【讨论】:

我继续设置这些值,但行为没有改变。奇怪的是,我可以看到客户端访问服务器并获得 401 响应。 HttpClient 似乎没有发送任何身份验证信息,只是收到 401 并退出。还有其他想法吗? 此外,我将运行客户端的应用程序池的身份更改为有权访问该服务的 Windows 帐户。这不会导致任何变化。我迷路了? @CraigAnderson 您能否在使用 HttpClient 发出请求之前检查 System.Security.Principal.WindowsIdentity.GetCurrent() 和 SecurityContext.IsWindowsIdentityFlowSuppressed() 的值?如果涉及到请求流,它是正确的。将 PreAuthenticate 设置为 true 会使 HttpClient 在初始请求中发送一个 Authorization 标头。如果响应为 401,则不会再尝试进行身份验证,因为可以发送的凭据已被拒绝。 @tpeczek 我在应用程序中打开了额外的日志记录。在客户端发送请求之前,我可以看到当前的 Windows 身份实际上是登录用户,并且 IsWindowsIndentityFlowSuppressed 的值设置为 false。这导致相信 httpclient 正在向服务传递身份验证? 我面临同样的问题,更新“legacyImpersonationPolicy”和“alwaysFlowImpersonationPolicy”标签的建议有效,但我想避免这种情况,因为它是机器范围内的更改。此链接表明每个应用程序池可以有一个 aspnet.config 文件,但我无法让它工作。有没有人有这方面的运气? weblogs.asp.net/owscott/archive/2011/12/01/…【参考方案2】:

我也遇到了同样的问题。感谢@tpeczek 所做的研究,我开发了以下解决方案:我没有使用 HttpClient(它创建线程并异步发送请求),而是使用了在同一个线程上发出请求的 WebClient 类。这样做使我能够将用户的身份从另一个 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 序列化程序相同。

【讨论】:

谢谢,这正是我需要的。 哇。 WindowsImpersonationContext。你拯救了这一天和我的理智。谢谢。 您是否尝试过使用异步对应物,例如 UploadDataAsync :-) 这对我不起作用。我仍然得到 401 Unauthorized。【参考方案3】:

这不起作用的原因是您需要双跳身份验证。

第一跳是 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/ (您将需要足够的访问权限来执行这些操作。)

请考虑如果允许任何服务器随意转发您的凭据会发生什么...为避免此安全问题,域控制器需要知道允许哪些帐户执行委派。

【讨论】:

【参考方案4】:

要模拟原始(经过身份验证的)用户,请在 Web.config 文件中使用以下配置:

<authentication mode="Windows" />
<identity impersonate="true" />

使用此配置,ASP.NET 始终模拟经过身份验证的用户,并且所有资源访问都使用经过身份验证的用户的安全上下文执行。

【讨论】:

以上是关于无法使用 HttpClient 对 ASP.NET Web Api 服务进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章

使用 HttpClient 压缩对 asp.net core 2 站点的请求的最佳方法是啥?

ASP.NET MVC 4 HttpClient() 问题

如何在使用 HttpClient 使用 Asp Net Web Api 的 Asp Net Mvc 中提供实时数据?

在 ASP.NET Core 应用程序中使用多个 HttpClient 对象

如何在ASP.NET Core 中使用IHttpClientFactory

asp.net core 使用HttpClientFactory Polly实现熔断降级