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

如何在.NET Core控制台应用中用Refit实现JWT认证并按Validity缓存Token

用Refit实现带Token缓存的API认证方案(.NET Core控制台应用)

刚好我之前在后台任务场景里做过类似的实现,用Refit来处理认证和Token缓存其实能做得很优雅,我给你一步步拆解具体方案:

1. 先定义认证相关的模型和Refit接口

首先把认证的请求、响应模型,以及对应的Refit接口写好,这是整个流程的基础:

// 认证请求体模型
public class AcquireTokenRequest
{
    public string Username { get; set; }
    public string Password { get; set; }
}

// 服务器返回的认证响应模型
public class TokenResponse
{
    public string Token { get; set; }
    public TimeSpan Validity { get; set; } // 假设返回的是可直接解析的时间跨度格式,比如"00:30:00"
    public DateTime ExpiresAt { get; set; }
}

// 认证接口的Refit定义
public interface IAuthApi
{
    [Post("/server/auth/token/acquire")]
    Task<TokenResponse> AcquireTokenAsync([Body] AcquireTokenRequest request);
}

2. 实现Token缓存与自动刷新逻辑

我们可以用.NET Core自带的IMemoryCache来缓存Token,同时根据返回的Validity值设置缓存过期时间,这样就不用手动判断Token是否失效了:

public class TokenManager
{
    private readonly IAuthApi _authApi;
    private readonly IMemoryCache _cache;
    private readonly AcquireTokenRequest _authRequest;
    private const string TokenCacheKey = "ApiAuth_BearerToken";
    private readonly SemaphoreSlim _semaphore = new(1, 1); // 加锁避免并发重复获取Token

    public TokenManager(IAuthApi authApi, IMemoryCache cache, IConfiguration config)
    {
        _authApi = authApi;
        _cache = cache;
        // 从配置文件读取账号密码,绝对不要硬编码!
        _authRequest = new AcquireTokenRequest
        {
            Username = config["ApiAuth:Username"],
            Password = config["ApiAuth:Password"]
        };
    }

    public async Task<string> GetValidTokenAsync()
    {
        // 先尝试从缓存取Token
        if (_cache.TryGetValue(TokenCacheKey, out string cachedToken))
        {
            return cachedToken;
        }

        // 缓存失效,加锁避免多线程重复请求Token
        await _semaphore.WaitAsync();
        try
        {
            // 再次检查缓存,防止等待期间已经有其他线程获取了Token
            if (_cache.TryGetValue(TokenCacheKey, out cachedToken))
            {
                return cachedToken;
            }

            // 调用认证接口获取新Token
            var tokenResponse = await _authApi.AcquireTokenAsync(_authRequest);
            
            // 根据返回的Validity设置缓存过期时间,也可以用ExpiresAt计算剩余有效期
            var cacheOptions = new MemoryCacheEntryOptions()
                .SetAbsoluteExpiration(tokenResponse.Validity);

            // 将新Token存入缓存
            _cache.Set(TokenCacheKey, tokenResponse.Token, cacheOptions);
            
            return tokenResponse.Token;
        }
        finally
        {
            _semaphore.Release();
        }
    }
}

3. 写一个自定义的请求拦截器(DelegatingHandler)

要让所有业务API请求自动带上Token,我们可以实现一个DelegatingHandler,在请求发送前自动获取有效Token并添加到请求头:

public class AuthHeaderHandler : DelegatingHandler
{
    private readonly TokenManager _tokenManager;

    public AuthHeaderHandler(TokenManager tokenManager)
    {
        _tokenManager = tokenManager;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // 获取有效Token
        var token = await _tokenManager.GetValidTokenAsync();
        // 添加Bearer认证头
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

        // 继续执行请求流程
        return await base.SendAsync(request, cancellationToken);
    }
}

4. 在DI容器中注册所有服务

控制台应用需要手动配置依赖注入,把我们的接口、Token管理器、拦截器都注册进去:

var builder = new HostBuilder()
    .ConfigureServices((hostContext, services) =>
    {
        // 注册内存缓存服务
        services.AddMemoryCache();
        
        // 注册认证API的Refit客户端
        services.AddRefitClient<IAuthApi>()
            .ConfigureHttpClient(c => c.BaseAddress = new Uri(hostContext.Configuration["ApiBaseUrl"]));

        // 注册Token管理器为单例
        services.AddSingleton<TokenManager>();

        // 注册自定义认证拦截器,再注册业务API的Refit客户端
        services.AddTransient<AuthHeaderHandler>();
        services.AddRefitClient<IBusinessApi>() // 这里替换成你的业务API接口
            .ConfigureHttpClient(c => c.BaseAddress = new Uri(hostContext.Configuration["ApiBaseUrl"]))
            .AddHttpMessageHandler<AuthHeaderHandler>();

        // 注册你的后台任务(用IHostedService实现)
        services.AddHostedService<YourBackgroundTask>();
    })
    .ConfigureAppConfiguration((hostingContext, config) =>
    {
        // 加载配置文件,比如appsettings.json
        config.AddJsonFile("appsettings.json", optional: false);
    });

await builder.RunConsoleAsync();

5. 后台任务中直接使用业务API

最后在你的后台任务里,直接注入业务API接口即可,Token的获取和添加都是自动完成的,完全不用在业务代码里关心认证逻辑:

public class YourBackgroundTask : BackgroundService
{
    private readonly IBusinessApi _businessApi;

    public YourBackgroundTask(IBusinessApi businessApi)
    {
        _businessApi = businessApi;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            // 调用业务API,自动带上有效Token
            var taskResult = await _businessApi.RunBackgroundTaskAsync();
            // 处理任务结果...

            await Task.Delay(TimeSpan.FromMinutes(10), stoppingToken);
        }
    }
}

额外的小提示:

  • 如果服务器返回的Validity是字符串格式(比如"30m"),需要先用TimeSpan.TryParse解析成TimeSpan类型。
  • 可以结合Polly库给Token获取逻辑添加重试机制,处理网络波动导致的认证请求失败。
  • 如果你的控制台应用是长期运行的,建议给缓存添加滑动过期或者监控Token过期的逻辑,确保不会出现认证失败的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:46:41