设置HttpClient的授权头部

825

我有一个HttpClient,用于访问REST API。但是我在设置Authorization头时遇到了问题。我需要将头设置为从OAuth请求中收到的令牌。

我看到了一些.NET的代码,建议如下操作:

httpClient.DefaultRequestHeaders.Authorization = new Credential(OAuth.token);

然而,在WinRT中没有Credential类。有没有任何想法如何设置Authorization头?


1
Credential类属于哪个命名空间? - kampsj
@kampsj 我不知道,因为这是一个在WinRT中不存在的.NET命名空间。 - Stephen Hynes
1
为什么不使用 request.Headers.Add("Authorization", token);呢? - ahll
1
@ahll 几年过去了,但现在会出现一个“Headers misused”消息的InvalidOperationException。 - Neil Monroe
1
@NeilMonroe 格式是什么? 授权:Bearer <token> - ahll
发布这个问题的人在 Stack Overflow 上已经九年没有出现了。可能已经退休或去世了。25 多个优秀答案中可能没有一个会被采纳 :-( - Roland
26个回答

1334

因此,实现这个的方法如下所示:

httpClient.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", "Your Oauth token");

34
你怎样获得“你的OAuth令牌”? - Squirrel5853
13
我所使用的是:client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "加密后的用户名/密码"); 我从Chrome扩展程序Advanced Rest Client中获取了加密后的用户名/密码。 - Red
13
@Red,供您参考,第二个参数是Base64编码的用户名和密码(并未加密)。 - n00b
6
我的应用程序长期以来一直开心地使用这个,然后突然之间我开始收到 RuntimeBinderException 的错误。为了让它重新运行起来,我不得不切换到 _httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer", "你的 OAuth 令牌");_。 - Craig
16
@kraeg,你列出的代码无法编译,你是不是想像这样连接最后两个字符串:client.DefaultRequestHeaders.Add("Authorization", "Bearer " + "Your Oauth token")?请注意,我的翻译不包含任何额外的解释或内容。 - TroySteven
显示剩余6条评论

463
request.DefaultRequestHeaders.Authorization = 
    new AuthenticationHeaderValue(
        "Basic", Convert.ToBase64String(
            System.Text.ASCIIEncoding.ASCII.GetBytes(
               $"{yourusername}:{yourpwd}")));

3
这不起作用,如果您检查Authorization头,则仅包含一个字符串Basic。 - Raffaeu
2
有人能解释一下为什么将用户名和密码转换为base64字符串很重要吗?它并没有提供真正的加密,那么为什么这很重要呢? - Jonathan Wood
4
因为这就是定义中所规定的使用方式。Basic认证并不提供加密,只提供足够的编码以避免在头部选择密码字符时出现问题。 - Richard
9
你在这里使用ASCII编码有什么特别的原因吗?我想,既然我们正在对其进行Base64编码,使用UTF8编码应该没有问题。我猜想,我想知道基本身份验证规范是否要求用户名:密码组合仅使用ASCII编码? - crush
4
System.Text.ASCIIEncoding.ASCII实际上是在父类Encoding中。因此,您可以使用System.Text.Encoding.ASCII代替。 - David Klempfner
显示剩余2条评论

121

我正在寻找解决这个问题的好方法,也在考虑同样的问题。希望这个答案能够帮助像我一样有相同问题的每个人。

using (var client = new HttpClient())
{
    var url = "https://www.theidentityhub.com/{tenant}/api/identity/v1";
    client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
    var response = await client.GetStringAsync(url);
    // Parse JSON response.
    ....
}

参考自https://www.theidentityhub.com/hub/Documentation/CallTheIdentityHubApi


3
我正在做与@willie完全相同的事情,但仍然从我的API得到401。 - SomethingOn
2
嗨@SomethingOn,我认为你没有得到正确的令牌密钥,所以你得到了401错误。我会在我的个人“提问”中分享我的方法,希望它能帮助你解决问题。顺便说一句,稍等片刻。 - Willie Cheng
34
不应该将 HttpClient 放在 using 代码块中。(是的,我知道这听起来很奇怪,但如果使用 using 而不是只回收 HttpClient,则会泄漏连接。) - Jonathan Allen
@JonathanAllen,如果你指的是这里描述的连接泄漏(https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/),值得注意的是,一些读者在文章评论中提到泄漏可能与如何处理响应有关,因为许多开发人员忘记处理HttpResponse本身,只处理了HttpClient - masiton
还要注意,如果您多次使用相同的标头名称调用 client.DefaultRequestHeaders.Add,它将抛出异常。(尝试添加多个相同的标头。) - Joshua Enfield
不应将 HttpClient 放入 using 语句中,因为这最终会导致套接字耗尽问题。原因在于:https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong - Tony

90

由于性能和端口耗尽问题,重用HttpClient实例是一个好的做法。因为没有任何答案提供这个解决方案(甚至会引导您走向不良实践:(),所以我在这里放置一个链接,指向我在类似问题上的答案:

https://dev59.com/Y10a5IYBdhLWcg3w6sZk#40707446

以下是一些正确使用HttpClient的资源:


17
端口耗尽问题不是闹着玩的。在质量保证阶段几乎从不会发生,但会影响任何生产环境中被大量使用的项目。 - Jonathan Allen
查看我的帖子以获取具体示例 https://dev59.com/02Uq5IYBdhLWcg3wSOfS#59052193 - emp
3
这个批评将默认授权设置在客户端中,居然被放到了如此靠后的帖子中,简直让人发笑。我一直在想为什么需要在客户端本身上设置授权。 - zireael9797
这些信息已经过时了。现在最好的做法是使用依赖注入或者一个IHttpClientFactory。这样就没有问题了,可以使用正确的URL和认证设置来配置httpclient本身。 - undefined

54
我建议你:
HttpClient.DefaultRequestHeaders.Add("Authorization", "Bearer <token>");

然后你可以像这样使用它:

var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
    responseMessage = await response.Content.ReadAsAsync<ResponseMessage>();
}

3
如果例如您的令牌每小时超时一次,则需要使用此解决方案更新HttpClient。我建议检查您的令牌是否仍然有效,否则刷新它并将其添加到HttpRequestMessage中。 - Johan Franzén

53
如果您想使用Bearer Token发送HttpClient请求,这段代码可能是一个不错的解决方案:
var requestMessage = new HttpRequestMessage
{
    Method = HttpMethod.Post,
    Content = new StringContent(".....", Encoding.UTF8, "application/json"),
    RequestUri = new Uri(".....")
};

requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "Your token");

var response = await _httpClient.SendAsync(requestMessage);

27

我正在设置令牌

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

它在一个端点上工作,但在另一个端点上不工作。问题是我在"bearer"上使用了小写字母b。更改后,现在两个API都可以正常运行。如果您甚至没有考虑它作为查找针的干草堆之一,那么很容易错过这样的问题。

请确保使用大写字母的"Bearer"


25

使用基本身份验证和JSON参数。

using (HttpClient client = new HttpClient())
{
    var request_json = "your json string";

    var content = new StringContent(request_json, Encoding.UTF8, "application/json");

    var authenticationBytes = Encoding.ASCII.GetBytes("YourUsername:YourPassword");

    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
            Convert.ToBase64String(authenticationBytes));
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    var result = await client.PostAsync("YourURL", content);

    var result_string = await result.Content.ReadAsStringAsync();
}

8
在这样的示例中,您不应包含禁用SSL证书检查的代码。人们可能会盲目复制您的代码,而没有意识到它的作用。我已经为您删除了那些行。 - ProgrammingLlama
1
不应将 HttpClient 放入 using 语句中,因为这最终会导致套接字耗尽问题。原因在于:https://www.aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong - Tony

22

对于现在(2021年)找到这个旧帖子的任何人,请查看有关HttpClientFactory的文档,该文档介绍了可注入的HttpClientFactory,并且还将在每个请求上重新运行以避免过期令牌,这使其对于Bearer Tokens、生成的客户端、池等非常有用。

太长不看:使用HttpClientFactory和一个DelegatingHandler来作为中间件处理所有传出请求与您配置的客户端。

这是我如何添加我的Azure Identity Bearer(由Azure进行管理),但当然您可以按照自己的方式获取令牌;

using Microsoft.Azure.Services.AppAuthentication;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

public class BearerTokenHandler : DelegatingHandler
    {
        public BearerTokenHandler(AzureServiceTokenProvider tokenProvider, string resource)
        {
            TokenProvider = tokenProvider;
            Resource = resource;
        }

        public AzureServiceTokenProvider TokenProvider { get; }
        public string Resource { get; }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (!request.Headers.Contains("Authorization"))
            {
                // Fetch your token here
                string token = await TokenProvider.GetAccessTokenAsync(Resource);
                request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
            }

            return await base.SendAsync(request, cancellationToken);
        }
    }

我在Startup中这样配置我的类型化客户端(由NSwag生成);

   var accessTokenProvider = new AzureServiceTokenProvider("<your-connection-string-for-access-token-provider>");

  builder.Services.AddHttpClient<IOrdersClient, OrdersClient>().ConfigureHttpClient(async conf =>
            {
                conf.BaseAddress = new Uri("<your-api-base-url>");
            }).AddHttpMessageHandler(() => new BearerTokenHandler(accessTokenProvider, "https://your-azure-tenant.onmicrosoft.com/api"));

然后您可以在任何需要的地方注入您的 IOrdersClient,所有请求都将具有Bearer。


16

如果你想要重复使用 HttpClient,建议不要使用 DefaultRequestHeaders,因为它们会随着每个请求发送。

你可以尝试这样做:

var requestMessage = new HttpRequestMessage
    {
        Method = HttpMethod.Post,
        Content = new StringContent("...", Encoding.UTF8, "application/json"),
        RequestUri = new Uri("...")
    };

requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", 
    Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes($"{user}:{password}")));

var response = await _httpClient.SendAsync(requestMessage);

3
是的,惊讶地发现这个答案很难找到。我猜许多人不怎么阅读文档,因为最佳实践是将 HttpClient 设为静态成员变量,以避免端口耗尽问题。如果我们谈论令牌/承载身份验证(像许多人在这里做的那样),那么使用 DefaultRequestHeaders 就更没有意义了,特别是因为这些令牌将不可避免地过期!因此,基于它来建立默认值只会适得其反。 - Jonas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接