使用Azure BlobContainerClient上传报错:颁发者验证失败,颁发者不匹配
问题场景
已通过Azure CLI使用服务主体成功上传文件至Azure Blob存储,CLI命令如下:
az login --service-principal -u <clientId> -p <clientSecret> --tenant <tenantId> && az storage blob upload \ --container-name "\$web" \ --account-name <accountName> \ --name test.txt \ --file test.txt \ --auth-mode login
但使用相同凭证通过Java SDK的BlobContainerClient上传时失败,核心代码片段:
val defaultAzureCredential: DefaultAzureCredential = DefaultAzureCredentialBuilder() .tenantId(azureConfig.azureTenantId) .build() val blobServiceClient = blobServiceClientBuilder .endpoint(azureConfig.storageAccountUrl) .credential(defaultAzureCredential) .buildClient() val blobContainerClient = blobServiceClient.getBlobContainerClient(STORAGE_ACCOUNT_CONTAINER) try { val fileName = "${appSlug}_${LocalDateTime.now().format(pattern)}.zip" val blobClient = blobContainerClient.getBlobClient(fileName) blobClient.upload(inputStream, true) }
报错信息:
return (<AuthenticationErrorDetail>Issuer validation failed. Issuer did not match.</AuthenticationErrorDetail> )
已确认CLI与DefaultAzureCredential获取的令牌颁发者均为https://sts.windows.net/<tenantId>/,以下是针对性解决思路:
解决思路
检查存储账户URL格式
确保azureConfig.storageAccountUrl严格符合https://<accountName>.blob.core.windows.net/格式,不要遗漏末尾斜杠,也不要包含额外路径。URL格式错误会导致SDK错误推断令牌受众,触发颁发者验证异常。显式指定令牌受众
DefaultAzureCredential自动推断受众可能出现偏差,可强制指定Blob存储的受众为https://storage.azure.com/,示例代码修改如下:val tokenCredential = DefaultAzureCredentialBuilder() .tenantId(azureConfig.azureTenantId) .build() // 显式配置Blob存储的令牌受众 val blobTokenCredential = TokenCredentialAdapter(tokenCredential) { request -> request.targetAudience = "https://storage.azure.com/" } val blobServiceClient = blobServiceClientBuilder .endpoint(azureConfig.storageAccountUrl) .credential(blobTokenCredential) .buildClient()若使用非全球Azure云(如Azure中国),需同步指定对应
Authority Host,比如AzureAuthorityHosts.AZURE_CHINA。验证服务主体权限范围
确认服务主体已被授予Blob数据权限(如Storage Blob Data Contributor),而非仅管理权限。CLI的--auth-mode login依赖Azure AD权限,SDK对权限的校验更严格,缺失数据权限可能触发验证失败。强制使用指定服务主体凭证
DefaultAzureCredential会尝试多种凭证来源(环境变量、托管标识、CLI缓存等),可能实际使用的凭证并非预期的服务主体。可直接使用ClientSecretCredential替代,避免自动推断问题:val credential = ClientSecretCredentialBuilder() .tenantId(azureConfig.azureTenantId) .clientId(azureConfig.clientId) .clientSecret(azureConfig.clientSecret) .build()对比令牌受众字段
用JWT解码工具(如jwt.io)分别解析CLI和SDK获取的令牌,检查aud(受众)字段是否一致。CLI令牌的受众应为https://<accountName>.blob.core.windows.net/或https://storage.azure.com/,若SDK令牌受众不符,需通过显式指定受众修正。确认云环境一致性
检查CLI当前云环境(执行az cloud show),确保SDK使用相同环境。若为非全球云,需在构建凭证时指定对应Authority Host。
内容的提问来源于stack exchange,提问作者Edward Rixon

