You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在HttpClient中处理Bearer Token过期问题?

自动处理Bearer Token过期的最佳方案

针对你的需求,使用DelegatingHandler扩展HttpClient的请求管道是最优解——它能让你在每次请求发送前自动检查并刷新Token,完全避免重复编写检查逻辑。

步骤1:实现线程安全的Token存储类

用来保存当前Token和过期时间,处理多线程场景下的并发问题:

public class TokenStore
{
    private string _accessToken;
    private DateTime _expirationTime;
    private readonly object _lockObj = new object();

    // 判断Token是否过期(提前1分钟刷新,避免刚好过期的请求失败)
    public bool IsTokenExpired()
    {
        lock (_lockObj)
        {
            return string.IsNullOrEmpty(_accessToken) || DateTime.UtcNow >= _expirationTime;
        }
    }

    public string GetCurrentToken()
    {
        lock (_lockObj)
        {
            return _accessToken;
        }
    }

    public void UpdateToken(string newToken, int expiresInSeconds)
    {
        lock (_lockObj)
        {
            _accessToken = newToken;
            _expirationTime = DateTime.UtcNow.AddSeconds(expiresInSeconds - 60);
        }
    }
}

步骤2:实现自定义DelegatingHandler

这是核心逻辑,负责在请求前检查Token、刷新Token,以及处理401重试:

public class BearerTokenHandler : DelegatingHandler
{
    private readonly TokenStore _tokenStore;
    private readonly HttpClient _tokenClient;
    private readonly string _tokenEndpoint;
    private readonly string _clientId;
    private readonly string _clientSecret;

    public BearerTokenHandler(TokenStore tokenStore, string tokenEndpoint, string clientId, string clientSecret)
    {
        _tokenStore = tokenStore;
        _tokenEndpoint = tokenEndpoint;
        _clientId = clientId;
        _clientSecret = clientSecret;
        // 单独用一个HttpClient获取Token,避免循环引用
        _tokenClient = new HttpClient();
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // 检查Token是否过期,过期则刷新
        if (_tokenStore.IsTokenExpired())
        {
            await RefreshToken(cancellationToken);
        }

        // 给请求添加Authorization头
        request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _tokenStore.GetCurrentToken());

        // 发送请求
        var response = await base.SendAsync(request, cancellationToken);

        // 处理401场景:如果检查Token后还是过期了(比如多线程并发),刷新后重试一次
        if (response.StatusCode == HttpStatusCode.Unauthorized)
        {
            await RefreshToken(cancellationToken);
            request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _tokenStore.GetCurrentToken());
            response = await base.SendAsync(request, cancellationToken);
        }

        return response;
    }

    // 从服务器获取新Token的方法,根据你的认证方式调整(比如Client Credentials/Password模式)
    private async Task RefreshToken(CancellationToken cancellationToken)
    {
        var formData = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("grant_type", "client_credentials"),
            new KeyValuePair<string, string>("client_id", _clientId),
            new KeyValuePair<string, string>("client_secret", _clientSecret)
        });

        var tokenResponse = await _tokenClient.PostAsync(_tokenEndpoint, formData, cancellationToken);
        tokenResponse.EnsureSuccessStatusCode();

        var tokenResult = await tokenResponse.Content.ReadFromJsonAsync<TokenDto>(cancellationToken);
        _tokenStore.UpdateToken(tokenResult.access_token, tokenResult.expires_in);
    }

    // 对应服务器返回的Token结构
    private class TokenDto
    {
        public string access_token { get; set; }
        public int expires_in { get; set; }
        public string token_type { get; set; }
    }
}

步骤3:重构你的HttpClientHelper

把自定义Handler注册到HttpClient中,确保HttpClient单例(避免频繁创建导致的端口耗尽):

public class HttpClientHelper
{
    private static HttpClient _httpClient;
    private static readonly TokenStore _tokenStore = new TokenStore();
    // 替换成你的实际配置
    private const string TokenEndpoint = "https://your-auth-server/token";
    private const string ClientId = "your-client-id";
    private const string ClientSecret = "your-client-secret";

    public static HttpClient Request()
    {
        if (_httpClient == null)
        {
            // 构建Handler链:自定义TokenHandler -> 原生HttpClientHandler
            var tokenHandler = new BearerTokenHandler(_tokenStore, TokenEndpoint, ClientId, ClientSecret);
            tokenHandler.InnerHandler = new HttpClientHandler();

            _httpClient = new HttpClient(tokenHandler);
            // 可以设置默认BaseAddress,简化后续请求
            // _httpClient.BaseAddress = new Uri("http://myurl.com");
        }
        return _httpClient;
    }
}

步骤4:使用方式

现在发起请求时完全不需要手动处理Token,逻辑自动执行:

var uri = "http://myurl.com/api/your-resource"; 
using (var request = new HttpRequestMessage(HttpMethod.Get, uri))
{
    using (var response = await HttpClientHelper.Request().SendAsync(request,
        new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token))
    {
        if (response.IsSuccessStatusCode)
        {
            // 处理你的业务逻辑
        }
    }
}

方案优势

  • 完全自动化:请求前自动检查、刷新Token,无需重复编写检查逻辑
  • 线程安全:Token存储加锁,避免多线程并发冲突
  • 容错性强:处理401重试,覆盖Token检查后刚好过期的边缘场景
  • 解耦性好:Token处理逻辑与业务请求逻辑完全分离

内容的提问来源于stack exchange,提问作者dbsoft

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 06:45:36