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

Azure App Service无法通过用户凭据/令牌连接Azure KeyVault问题求助

解决方案:使用用户OIDC令牌访问Azure KeyVault

你的问题核心在于 DefaultAzureCredential 并不会自动读取Web应用中通过AddInMemoryTokenCaches缓存的用户OIDC令牌。它的凭证优先级链(环境变量、托管标识、共享令牌缓存等)里的共享令牌缓存是针对桌面/CLI场景的,和Web应用的内存缓存不是同一个存储,所以才会出现所有凭证都不可用的错误。

下面是具体的修复步骤:

1. 更新API权限Scope

首先,你需要在Startup.cs中添加Azure KeyVault的访问Scope,替换原来仅有的user.read(这是Graph API的权限,和KeyVault无关):

.EnableTokenAcquisitionToCallDownstreamApi(new string[] { "https://vault.azure.net/user_impersonation" })

这个Scope是KeyVault允许用户身份模拟访问的标准权限,必须添加才能获取到有效的访问令牌。

2. 在Azure AD应用注册中配置KeyVault权限

确保你的应用注册已经添加了KeyVault的API权限:

  • 登录Azure Portal,找到你的Azure AD应用注册
  • 进入API权限页面,点击添加权限
  • 搜索并选择Azure Key Vault,添加user_impersonation权限
  • 点击授予管理员同意(如果你的租户要求管理员审批)

3. 使用用户令牌创建SecretClient

不再依赖DefaultAzureCredential,而是手动获取用户的访问令牌,并用它来初始化SecretClient。具体代码如下:

在Controller中注入ITokenAcquisition

private readonly ITokenAcquisition _tokenAcquisition;
private readonly AzureKeyVaultSettings _kvSettings;

public HomeController(ITokenAcquisition tokenAcquisition, IOptions<AzureKeyVaultSettings> kvSettings)
{
    _tokenAcquisition = tokenAcquisition;
    _kvSettings = kvSettings.Value;
}

获取令牌并初始化SecretClient

public async Task<IActionResult> Index()
{
    try
    {
        // 获取当前登录用户的KeyVault访问令牌
        var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(
            new[] { "https://vault.azure.net/user_impersonation" });

        // 用令牌创建自定义TokenCredential
        var userTokenCredential = new UserTokenCredential(accessToken);
        
        // 初始化SecretClient
        var client = new SecretClient(
            new Uri(_kvSettings.KeyVaultBaseUrl), 
            userTokenCredential);

        // 读取密钥列表
        var secretList = new List<SecretProperties>();
        await foreach (var secret in client.GetPropertiesOfSecretsAsync())
        {
            secretList.Add(secret);
        }

        return View(secretList);
    }
    catch (Exception ex)
    {
        _logger.LogCritical(ex, "Failed to retrieve KeyVault secrets");
        return View("Error", ex);
    }
}

// 自定义TokenCredential实现,直接返回已获取的用户令牌
private class UserTokenCredential : TokenCredential
{
    private readonly string _accessToken;

    public UserTokenCredential(string accessToken)
    {
        _accessToken = accessToken;
    }

    public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
    {
        // 令牌有效期可以从实际令牌中解析,这里简化为1小时
        return new AccessToken(_accessToken, DateTimeOffset.UtcNow.AddHours(1));
    }

    public override ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
    {
        return new ValueTask<AccessToken>(GetToken(requestContext, cancellationToken));
    }
}

为什么原来的方案不生效?

DefaultAzureCredential的设计目标是在不同环境中自动选择合适的凭证(本地用VS/CLI,服务器用托管标识等),但它不会读取Web应用中通过Microsoft Identity Web库缓存的用户令牌。这些令牌存储在AddInMemoryTokenCaches提供的缓存中,只有ITokenAcquisition服务可以访问,所以必须手动获取并使用。

另外,你之前的代码中没有请求KeyVault的权限Scope,即使DefaultAzureCredential能找到令牌,也没有访问KeyVault的权限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 16:12:38