如何让HttpClient与请求一起传递凭据?

adr*_*nks 153 c# impersonation windows-security asp.net-web-api

我有一个与Windows服务对话的Web应用程序(在IIS中托管).Windows服务使用ASP.Net MVC Web API(自托管),因此可以使用JSON通过http进行通信.Web应用程序配置为进行模拟,其想法是向Web应用程序发出请求的用户应该是Web应用程序用来向服务发出请求的用户.结构如下所示:

(以红色突出显示的用户是以下示例中引用的用户.)


Web应用程序使用以下命令向Windows服务发出请求HttpClient:

var httpClient = new HttpClient(new HttpClientHandler() 
                      {
                          UseDefaultCredentials = true
                      });
httpClient.GetStringAsync("http://localhost/some/endpoint/");
Run Code Online (Sandbox Code Playgroud)

这会向Windows服务发出请求,但不会正确传递凭据(服务将用户报告为IIS APPPOOL\ASP.NET 4.0).这不是我想要发生的事情.

如果我将上述代码更改为使用WebClient,则会正确传递用户的凭据:

WebClient c = new WebClient
                   {
                       UseDefaultCredentials = true
                   };
c.DownloadStringAsync(new Uri("http://localhost/some/endpoint/"));
Run Code Online (Sandbox Code Playgroud)

使用上述代码,服务将用户报告为向Web应用程序发出请求的用户.

我执行错误HttpClient导致它无法正确传递凭据(或者它是一个错误HttpClient),我做错了什么

我想使用HttpClient它的原因是它有一个与s兼容的异步API Task,而WebClientasyc API需要用事件处理.

Sea*_*ean 118

您可以配置HttpClient为自动传递以下凭据:

myClient = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true })
Run Code Online (Sandbox Code Playgroud)

  • 我知道该怎么做.行为不是我想要的(如问题中所述) - "这会向Windows服务发出请求,但不会正确传递凭据(该服务将用户报告为IIS APPPOOL\ASP.NET 4.0).不是我想要发生的事." (11认同)
  • 这似乎解决了我的问题,即iis只启用了Windows身份验证.如果你只需要传递一些合法的证书,那就应该这样做. (4认同)

Jos*_*hua 59

我也有同样的问题.由于@tpeczek在以下SO文章中所做的研究,我开发了一个同步解决方案:无法使用HttpClient对ASP.NET Web Api服务进行身份验证

我的解决方案使用了一个WebClient,正如您正确指出的那样,它可以毫无问 原因HttpClient不起作用是因为Windows安全性禁用了在模拟帐户下创建新线程的能力(参见上面的SO文章.) HttpClient通过任务工厂创建新线程,从而导致错误. WebClient另一方面,在同一个线程上同步运行,从而绕过规则并转发其凭据.

尽管代码有效,但缺点是它不能正常工作.

var wi = (System.Security.Principal.WindowsIdentity)HttpContext.Current.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序列化程序相同.


Bla*_*Spy 24

你要做的是让NTLM将身份转发到下一个服务器,这是它无法做到的 - 它只能进行模拟,只能让你访问本地资源.它不会让你跨越机器边界.Kerberos身份验证通过使用故障单支持委派(您需要),并且当链中的所有服务器和应用程序都已正确配置并且在域上正确设置Kerberos时,可以转发故障单.因此,简而言之,您需要从使用NTLM切换到Kerberos.

有关Windows可用的身份验证选项以及它们的工作原理,请访问:http: //msdn.microsoft.com/en-us/library/ff647076.aspx

  • "_NTLM将身份转发到下一个服务器上,它无法做到这一点" - 在使用"WebClient"时它是怎么做到的?这是我不明白的事情 - 如果不可能的话,它怎么做呢? (3认同)
  • 使用Web客户端时,客户端和服务器之间仍然只有一个连接。它可以模拟该服务器上的用户(1跳),但不能将这些凭据转发到另一台计算机上(2跳-客户端到服务器到第二台服务器)。为此,您需要委派。 (2认同)
  • @BlackSpy:我有很多Windows身份验证经验.我想要了解的是为什么`WebClient`可以传递NTLM凭证,但是`HttpClient`不能.我可以单独使用ASP.Net模拟来实现这一点,而不必使用Kerberos或存储用户名/密码.但是***只适用于`WebClient`. (2认同)

小智 14

好的,感谢上面的所有贡献者.我使用的是.NET 4.6,我们也遇到了同样的问题.我花时间调试System.Net.Http,特别是HttpClientHandler,并发现以下内容:

    if (ExecutionContext.IsFlowSuppressed())
    {
      IWebProxy webProxy = (IWebProxy) null;
      if (this.useProxy)
        webProxy = this.proxy ?? WebRequest.DefaultWebProxy;
      if (this.UseDefaultCredentials || this.Credentials != null || webProxy != null && webProxy.Credentials != null)
        this.SafeCaptureIdenity(state);
    }
Run Code Online (Sandbox Code Playgroud)

所以在评估了ExecutionContext.IsFlowSuppressed()可能是罪魁祸首后,我将我们的模拟代码包装如下:

using (((WindowsIdentity)ExecutionContext.Current.Identity).Impersonate())
using (System.Threading.ExecutionContext.SuppressFlow())
{
    // HttpClient code goes here!
}
Run Code Online (Sandbox Code Playgroud)

里面的代码SafeCaptureIdenity(不是我的拼写错误),抓住WindowsIdentity.Current()了我们冒充身份的身份.这是因为我们现在正在抑制流动.由于使用/ dispose,这在调用后重置.

它现在似乎对我们有用,p!

  • 非常感谢你做这个分析。这也解决了我的情况。现在我的身份被正确地传递给另一个 Web 应用程序!你节省了我几个小时的工作!我很惊讶它的滴答计数并没有更高。 (2认同)

sco*_*192 8

在.NET Core中,我设法System.Net.Http.HttpClient使用UseDefaultCredentials = true来通过来将经过身份验证的用户的Windows凭据传递到后端服务WindowsIdentity.RunImpersonated

HttpClient client = new HttpClient(new HttpClientHandler { UseDefaultCredentials = true } );
HttpResponseMessage response = null;

if (identity is WindowsIdentity windowsIdentity)
{
    await WindowsIdentity.RunImpersonated(windowsIdentity.AccessToken, async () =>
    {
        var request = new HttpRequestMessage(HttpMethod.Get, url)
        response = await client.SendAsync(request);
    });
}
Run Code Online (Sandbox Code Playgroud)


小智 5

在我在 Windows 服务中设置了可以访问互联网的用户后,它对我有用。

在我的代码中:

HttpClientHandler handler = new HttpClientHandler();
handler.Proxy = System.Net.WebRequest.DefaultWebProxy;
handler.Proxy.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
.....
HttpClient httpClient = new HttpClient(handler)
.... 
Run Code Online (Sandbox Code Playgroud)