在Visual Studio 2022中运行.NET 6 Web API时获取令牌及缓存方案咨询
我有一个部署在Azure App Service上的.NET 6 Web API应用,当前使用Azure托管身份获取令牌,依赖Azure.Identity版本1.7.0,相关代码如下:
var tokenCredential = new ChainedTokenCredential( new ManagedIdentityCredential("", new TokenCredentialOptions { Retry = { Delay = TimeSpan.FromSeconds(2), MaxDelay = TimeSpan.FromSeconds(16), MaxRetries = 5, Mode = RetryMode.Exponential } }), new AzureCliCredential() ); var accessToken = await tokenCredential.GetTokenAsync(new TokenRequestContext(new[] { _configuration.GetSection("AzureAd:Scope")?.Value }));
目前该应用仅能在Azure App Service环境下运行(因应用服务身份已在Azure AD应用中注册),且未找到开箱即用的令牌缓存方案。现需咨询以下两个问题:
- 在Visual Studio 2022中运行该Web应用时,如何获取令牌?
- 是否存在开箱即用的令牌缓存方案?若没有,有哪些可选方案?
1. Visual Studio 2022本地运行获取令牌
你的代码已使用ChainedTokenCredential,它会按顺序尝试链中的凭证。要在Visual Studio 2022本地运行时获取令牌,只需在链中添加VisualStudioCredential即可,修改后的代码如下:
var tokenCredential = new ChainedTokenCredential( new ManagedIdentityCredential("", new TokenCredentialOptions { Retry = { Delay = TimeSpan.FromSeconds(2), MaxDelay = TimeSpan.FromSeconds(16), MaxRetries = 5, Mode = RetryMode.Exponential } }), new VisualStudioCredential(), // 添加Visual Studio凭证支持 new AzureCliCredential() );
添加后,本地运行时ChainedTokenCredential会尝试使用Visual Studio中登录的Azure账号获取令牌。需确保你在Visual Studio中登录的账号拥有目标资源的访问权限,且AzureAd:Scope配置的范围对该账号有效。
保留ChainedTokenCredential的好处是无需修改代码,即可在本地和Azure生产环境间无缝切换。
2. 令牌缓存方案
Azure.Identity库本身没有开箱即用的令牌缓存实现,因为不同应用场景(单实例、多租户、无状态API等)对缓存的需求差异极大。以下是几种实用的可选方案:
内存缓存(单实例简单场景)
适合单实例部署、无状态要求不高的应用,直接使用.NET自带的IMemoryCache:
private readonly IMemoryCache _cache; // 构造函数注入IMemoryCache public TokenService(IMemoryCache cache) { _cache = cache; } public async Task<string> GetCachedTokenAsync(IConfiguration configuration) { var scope = configuration.GetSection("AzureAd:Scope")?.Value; var cacheKey = $"Token_{scope}"; if (_cache.TryGetValue(cacheKey, out string cachedToken)) { return cachedToken; } var tokenCredential = new ChainedTokenCredential( new ManagedIdentityCredential("", new TokenCredentialOptions { Retry = { Delay = TimeSpan.FromSeconds(2), MaxDelay = TimeSpan.FromSeconds(16), MaxRetries = 5, Mode = RetryMode.Exponential } }), new VisualStudioCredential(), new AzureCliCredential() ); var accessToken = await tokenCredential.GetTokenAsync(new TokenRequestContext(new[] { scope })); // 预留5分钟缓冲时间,避免令牌过期前的请求失败 var cacheOptions = new MemoryCacheEntryOptions() .SetAbsoluteExpiration(accessToken.ExpiresOn - TimeSpan.FromMinutes(5)); _cache.Set(cacheKey, accessToken.Token, cacheOptions); return accessToken.Token; }
该方案实现简单,但应用重启或扩容后缓存会丢失,不适合多实例部署场景。
分布式缓存(多实例生产场景)
如果是多实例部署的应用,建议使用分布式缓存(如Azure Redis Cache、SQL Server分布式缓存),确保所有实例共享缓存状态。以Azure Redis Cache为例,使用IDistributedCache:
private readonly IDistributedCache _distributedCache; // 构造函数注入IDistributedCache public TokenService(IDistributedCache distributedCache) { _distributedCache = distributedCache; } public async Task<string> GetCachedTokenAsync(IConfiguration configuration) { var scope = configuration.GetSection("AzureAd:Scope")?.Value; var cacheKey = $"Token_{scope}"; var cachedToken = await _distributedCache.GetStringAsync(cacheKey); if (!string.IsNullOrEmpty(cachedToken)) { return cachedToken; } var tokenCredential = new ChainedTokenCredential( new ManagedIdentityCredential("", new TokenCredentialOptions { Retry = { Delay = TimeSpan.FromSeconds(2), MaxDelay = TimeSpan.FromSeconds(16), MaxRetries = 5, Mode = RetryMode.Exponential } }), new VisualStudioCredential(), new AzureCliCredential() ); var accessToken = await tokenCredential.GetTokenAsync(new TokenRequestContext(new[] { scope })); var expiration = accessToken.ExpiresOn - DateTimeOffset.UtcNow - TimeSpan.FromMinutes(5); await _distributedCache.SetStringAsync(cacheKey, accessToken.Token, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = expiration }); return accessToken.Token; }
需提前配置分布式缓存的连接字符串,确保所有应用实例都能访问到缓存服务。
自定义缓存实现
如果有特殊需求(如租户隔离、精细的缓存过期策略),可以封装独立的令牌缓存服务,结合业务场景定制缓存逻辑。例如针对多租户场景,在缓存键中加入租户ID,实现租户级别的缓存隔离。
内容的提问来源于stack exchange,提问作者user584018

