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

如何通过DeviceCode认证访问Azure Blob Storage及多资源令牌静默获取

使用DeviceCode认证获取多资源令牌并创建BlobServiceClient的解决方案

问题背景

在IoT设备配置流程中,需要通过DeviceCode认证让技术人员授权应用访问Azure Blob Storage并下载文件。现有代码可成功获取IoT Central的访问令牌,但存在以下问题:

  • 无法复用首次认证的Refresh Token,避免重复用户交互
  • 不清楚如何静默获取Blob Storage的访问令牌
  • 不知道如何用认证结果创建BlobServiceClient
  • 设备无浏览器,必须使用DeviceCode认证方式

核心解决方案

MSAL库会自动处理Refresh Token的缓存与复用,无需手动操作。核心要点:

  • 复用同一个IPublicClientApplication实例,保证令牌缓存共享
  • 通过AcquireTokenSilent方法静默获取其他资源的令牌,仅在缓存无有效令牌时触发交互(首次认证后无需重复操作)
  • 针对Blob Storage的专属scope调用静默获取流程

修改后的完整代码示例

1. 类成员与PCA初始化

将IPublicClientApplication设为类成员,避免每次获取令牌时重新创建导致缓存丢失:

public class Constants
{
    public const string ClientApplicationId = "app-id";
    public const string TenantId = "tenant-id";
    public const string AppName = "registered-desktop-app-name";
    public const string ApiVersion = "2022-07-31";
    public const string DeviceTemplateId = "dtmi:modelDefinition:e2eStandard;1";
    public static string[] IOTCScopes = new[] { "https://apps.azureiotcentral.com/.default" };
    public static string[] StorageScopes = new[] { "https://storage.azure.com/.default" };
}

private IPublicClientApplication _pca;
private CancellationTokenSource _cancellationTokenSource;
private AuthenticationResult? _iotcAuthResult;
private AuthenticationResult? _blobSAuthResult;
public string AuthInstruction { get; set; }

// 在构造函数中初始化PCA实例
public YourClassName()
{
    _pca = PublicClientApplicationBuilder
        .Create(Constants.ClientApplicationId)
        .WithAuthority($"https://login.microsoftonline.com/{Constants.TenantId}") // 必须指定具体租户,DeviceCode不支持common/consumers端点
        .Build();
}

2. 通用令牌获取方法

封装支持多scope的令牌获取逻辑,自动处理静默获取与DeviceCode交互:

async Task<AuthenticationResult> GetTokenForScopes(string[] scopes)
{
    _cancellationTokenSource = new CancellationTokenSource();
    var accounts = await _pca.GetAccountsAsync();

    try
    {
        // 优先从缓存静默获取令牌
        return await _pca.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
            .ExecuteAsync(_cancellationTokenSource.Token);
    }
    catch (MsalUiRequiredException)
    {
        // 缓存无有效令牌时,触发DeviceCode认证
        return await AcquireByDeviceCodeAsync(scopes);
    }
}

private async Task<AuthenticationResult> AcquireByDeviceCodeAsync(string[] scopes)
{
    try
    {
        var result = await _pca.AcquireTokenWithDeviceCode(scopes, deviceCodeResult =>
        {
            this.AuthInstruction = deviceCodeResult.Message;
            return Task.CompletedTask;
        }).ExecuteAsync(_cancellationTokenSource.Token);

        return result;
    }
    catch (MsalServiceException ex)
    {
        Debug.WriteLine($"服务端错误: {ex.Message}");
    }
    catch (OperationCanceledException ex)
    {
        Debug.WriteLine($"操作已取消: {ex.Message}");
    }
    catch (MsalClientException ex)
    {
        Debug.WriteLine($"客户端错误: {ex.Message}");
    }
    return null;
}

3. 认证流程优化

修改Login方法,实现一次交互获取多资源令牌:

public async Task LoginAzureAD()
{
    // 获取IoT Central令牌(首次触发DeviceCode交互)
    _iotcAuthResult = await GetTokenForScopes(Constants.IOTCScopes);

    if (_iotcAuthResult != null)
    {
        AuthInstruction = $"已认证用户 {_iotcAuthResult.Account.Username} (IoT Central)";
    }
    else
    {
        AuthInstruction = "IoT Central认证失败,请重试或联系管理员";
        return;
    }

    // 静默获取Blob Storage令牌,无需再次用户交互
    _blobSAuthResult = await GetTokenForScopes(Constants.StorageScopes);

    if (_blobSAuthResult != null)
    {
        AuthInstruction = $"已获取Blob Storage访问权限,用户: {_blobSAuthResult.Account.Username}";
    }
    else
    {
        AuthInstruction = "Blob Storage认证失败,请重试或联系管理员";
    }
}

4. 创建BlobServiceClient

使用Blob Storage的访问令牌创建客户端:

public BlobServiceClient CreateBlobServiceClient()
{
    if (_blobSAuthResult == null)
        throw new InvalidOperationException("Blob Storage未完成认证");

    // 自定义TokenCredential适配Azure SDK接口
    var tokenCredential = new BlobTokenCredential(_blobSAuthResult.AccessToken);
    // 替换为你的存储账户URL
    string storageAccountUrl = "https://yourstorageaccount.blob.core.windows.net/";
    
    return new BlobServiceClient(new Uri(storageAccountUrl), tokenCredential);
}

// 实现TokenCredential接口,适配MSAL令牌
private class BlobTokenCredential : TokenCredential
{
    private readonly string _accessToken;

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

    public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
    {
        // 令牌有效期通常为1小时,这里设为59分钟留缓冲
        return new AccessToken(_accessToken, DateTimeOffset.UtcNow.AddMinutes(59));
    }

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

关键注意事项

  • 应用权限配置:需在Azure AD应用注册中添加Blob Storage的API权限(如Storage Blob Data Reader),并确保管理员已同意
  • 租户限制:DeviceCode流不支持common或consumers租户,必须指定具体的TenantId
  • 令牌持久化:默认缓存为内存级,若需设备重启后保留认证状态,需实现ITokenCacheSerializer序列化缓存

内容的提问来源于stack exchange,提问作者Duncan Groenewald

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 14:34:57