【问题标题】:NetWorkCredentials not showing up in headersNetWorkCredentials 未显示在标题中
【发布时间】:2022-01-09 07:59:31
【问题描述】:

我有两个项目:一个 Web API 项目和一个客户端项目。

在客户端应用程序中,我像这样配置我的HttpClient

services.AddHttpClient<TrackAndTraceClient>()
    .ConfigureHttpClient(httpClient =>
    {
        httpClient.BaseAddress = new Uri(settings.BaseUrl);
        httpClient.Timeout = TimeSpan.FromMinutes(5);
    })
    .ConfigurePrimaryHttpMessageHandler(serviceProvider =>
    {
        return new HttpClientHandler()
        {
            Credentials = new NetworkCredential(settings.Username, settings.Password),
        };
    });

然后在我的调用 API 的类中:

public TrackAndTraceClient(IHttpClientFactory httpClientFactory, IOptions<TrackAndTraceSettings> settings)
{
    HttpClient = httpClientFactory.CreateClient(nameof(TrackAndTraceClient));
    Settings = settings.Value;
}

我的 Web API 站点使用this article 中描述的技术实现基本身份验证。但是我的代码抛出异常,因为没有找到 Authorization 标头。

public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {

        // ...

        if (!Request.Headers.TryGetValue("Authorization", out StringValues authHeaderValues))
            throw new Exception("Missing Authorization header");

        // ...
    }
}

我只能通过将以下代码添加到调用 API 的类中来使其工作:

HttpClient.DefaultRequestHeaders.Add("ContentType", "application/json");
byte[] credentialsData = Encoding.UTF8.GetBytes($"{Settings.Username}:{Settings.Password}");
string credentials = Convert.ToBase64String(credentialsData);
HttpClient.DefaultRequestHeaders.Add("Authorization", $"Basic {credentials}");

谁能告诉我为什么需要最后一段代码?为什么使用NetworkCredential 设置凭据似乎没有任何作用?以及如何更改我的 Web API 以使其使用原始方式指定的凭据?

请注意,我还调用了第三方 API,并且客户端的配置方式与我的第一个代码块中的完全相同。所以我知道这可以发挥作用。

【问题讨论】:

  • 如果不查看对第三方系统和您的服务器的实际 HTTP 请求和响应,就很难判断。例如,您的 WebAPI 是否返回 HTTP 401 Unauthorized with WWW-Authenticate: Basic header?除非得到指示,否则我不希望 HttpClientHandler 参与授权过程。
  • @ZdeněkJelínek:我正在检查我的基本身份验证处理程序中的标头(请参阅this article)。所以 Web API 会返回我指出的任何内容。
  • 这里是凭记忆走的,请耐心等待。您正在创建一个类型化的 HttpClient (.AddHttpClient&lt;&gt;()),而不是命名的 HttpClient (.AddHttpClient(string))...所以当您注入 IHttpClientFactory 并运行 .CreateClient(string) 时,它不会找到您的客户端设置。使用类型化客户端,您根本不需要使用IHttpClientFactory,而只需直接注入HttpClient
  • @GTHvidsten:是的,看起来你可能对不需要 IHttpClientFactory 进行依赖注入是正确的。我目前正在对此进行审查。但是,除了授权标头之外,我的代码仍然获得由我的代码配置的HttpClient 实例。所以这个建议似乎与我遇到的问题无关。
  • @pfx:天哪!你说的对!我有点困惑,因为我认为 401 响应是由我的代码返回 AuthenticateResult.Fail("Access Denied") 触发的。但实际上,添加该响应标头,现在它可以工作了。我不太明白,但你似乎解决了这个问题。随意写出来作为答案,我会接受的。

标签: c# asp.net authentication asp.net-web-api .net-6.0


【解决方案1】:

来自我们在 cmets 部分的对话 -

您的BasicAuthenticationHandler 的实现缺少添加WWW-Authenticate HTTP 标头(带有值Basic)。

这是 HttpClient 在收到 401 Unauthorized 响应时做出反应以包含 Authorization HTTP 标头的标头。

要解决此问题,请将下面的行添加到 BasicAuthenticationHandler

Response.Headers.Add("WWW-Authenticate", "Basic");

现在NetworkCredentials 将进入Authorization HTTP 标头。


简而言之,它的工作原理并不完整;
HttpClient 发出请求(没有Authorization 标头),并收到401 Unauthorized 响应与WWW-Authenticate HTTP 标头组合时,它将使用配置的凭据进行第二次尝试 - 如果有的话 - 在Authorization HTTP 标头。


为简单起见,您可能希望一次包含 Authorization,而不依赖于 NetworkCredentialsWWW-Authenticate HTTP 标头。

services.AddHttpClient<TrackAndTraceClient>()
    .ConfigureHttpClient(httpClient =>
    {
        httpClient.BaseAddress = new Uri(settings.BaseUrl);
        httpClient.Timeout = TimeSpan.FromMinutes(5);

        // Add below to your existing code.
        var digest = Convert.ToBase64String(
            Encoding.UTF8.GetBytes($"{settings.Username}:{settings.Password}")
            );  
        httpClient.DefaultRequestHeaders.Add("Authorization", $"Basic {digest}");
        HttpClient.DefaultRequestHeaders.Add("ContentType", "application/json");        
    });

【讨论】:

  • 谢谢。但是您知道this article 中的处理程序是如何工作的吗?我看不到它在哪里设置 WWW-Authenticate 标头。
  • 是否正确地说使用NetworkCredential 设置凭据效率较低,因为它需要两个请求(一个获取401,第二个设置标头)?并且直接设置标头会更有效,节省其中一个请求?
  • 关于效率是的;这就是为什么大多数代码示例自己设置 Authorization HTTP 标头 - 记住我的第一个/删除的答案;)
  • 是的,好的。知道了这一点,我可能会从您的第一个答案中实现代码。但我想解释为什么需要它。它为我而来。谢谢。 Stackoverflow 还不让我接受答案。
  • @JonathanWood 我在此答案中包含了该部分,只是为了使其更完整以供将来参考左右。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多