我们在 Azure 环境中使用 Azure 系统分配的托管身份来对 Azure 云资源之间的调用进行身份验证。
我们在注册
DefaultAzureCredential
时尝试实现 BlobServiceClient
:
services.AddAzureClients(builder =>
{
builder.UseCredential(new DefaultAzureCredential());
string url = string.Format("https://{0}.blob.core.windows.net/{1}",
blobStorageOptions.AccountName,
blobStorageOptions.ContainerName);
builder.AddBlobServiceClient(new Uri(url));
});
根据 文档,
DefaultAzureCredential
应执行各种 TokenCredential
实现,直到其中之一成功。 如果没有成功,则会抛出 CredentialUnavailableException
。
DefaultAzureCredential
身份验证的默认顺序是在调用 ManagedIdentityCredential
之前调用 VisualStudioCredential
。 因此,当应用程序在本地 VS 中运行时,ManagedIdentityCredential
should 会失败,而 VisualStudioCredential
最终成功并且代码按设计运行。
但是,应用程序会在本地失败,并在调用
CredentialUnavailableException
时抛出 ManagedIdentityCredential
,而不是继续执行应该成功的 VisualStudioCredential
。 当应用程序部署到我们的 Azure 环境时,它就会成功。 请注意,我们的 VS Tools>Options>AzureServiceAuthentication 已正确配置,并且帐户具有所需的权限。
我们也用过这个方法:
services.AddAzureClients(builder =>
{
TokenCredential credential = new ChainedTokenCredential(
new ManagedIdentityCredential(),
new VisualStudioCredential());
builder.UseCredential(credential);
string url = string.Format("https://{0}.blob.core.windows.net/{1}",
blobStorageOptions.AccountName,
blobStorageOptions.ContainerName);
builder.AddBlobServiceClient(new Uri(url));
});
如此处推荐的 适用于 .NET 的 Azure 身份库中的凭据链(以获得更好的性能)。
这也会以同样的方式在本地失败(
ManagedIdentityCredential
抛出异常)。
如果我们更改前面代码的顺序(
VisualStudioCredential
在ManagedIdentityCredential
之前),应用程序在本地和Azure环境中都会成功:
TokenCredential credential = new ChainedTokenCredential(
new VisualStudioCredential(),
new ManagedIdentityCredential());
我们可以使用之前的实现,但是文档中描述的效率提升将无法实现。
我唯一的结论是
DefaultAzureCredential
没有按设计工作。我是不是错过了什么?
我尝试使用以下代码在 Azure 门户中使用
DefaultAzureCredential
和 ManagedIdentityCredential
在本地对 Azure 存储进行身份验证。
我已将 Storage Blob Data Contributor 角色分配给 Azure 存储中的
Service Principal
和 Web App
,以避免出现下面所示的错误。
程序.cs:
using Azure.Core;
using Azure.Identity;
using Microsoft.Extensions.Azure;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSwaggerGen();
builder.Services.AddAzureClients(clientBuilder =>
{
TokenCredential credential;
if (builder.Environment.IsDevelopment())
{
credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions
{
});
}
else
{
credential = new ManagedIdentityCredential();
}
clientBuilder.UseCredential(credential);
string blobUri = "https://<StorageName>.blob.core.windows.net";
clientBuilder.AddBlobServiceClient(new Uri(blobUri));
});
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Web API V1");
c.RoutePrefix = app.Environment.IsDevelopment() ? "swagger" : string.Empty;
});
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
BlobController.cs:
using Azure.Storage.Blobs;
using Microsoft.AspNetCore.Mvc;
namespace WebApplication19.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class BlobController : ControllerBase
{
private readonly BlobServiceClient _blobServiceClient;
public BlobController(BlobServiceClient blobServiceClient)
{
_blobServiceClient = blobServiceClient;
}
[HttpGet("list-blobs")]
public async Task<IActionResult> ListBlobsAsync()
{
try
{
var containerClient = _blobServiceClient.GetBlobContainerClient("kamcontainer");
var blobItems = new List<string>();
await foreach (var blobItem in containerClient.GetBlobsAsync())
{
blobItems.Add(blobItem.Name);
}
return Ok(blobItems);
}
catch (Exception ex)
{
return StatusCode(500, $"Internal server error: {ex.Message}");
}
}
}
}
本地输出:
将存储 Blob 数据贡献者角色分配给服务主体后,我成功检索了 Blob 列表。
Azure Web 应用程序输出: