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

Microsoft Graph:如何避免每次启动进程时重复进行授权验证

解决方案

核心思路

  • 可以完全避免每次启动的重复身份验证:通过微软身份验证库(MSAL)的持久化令牌缓存,将首次验证后获得的刷新令牌存储到本地磁盘,后续进程启动时直接读取缓存的刷新令牌换取访问令牌,无需重新走设备码验证流程。
  • 默认的DeviceCodeCredential自带的缓存是内存级别的,进程关闭后就会丢失,所以需要自己基于带持久缓存的PublicClientApplication封装适配Graph SDK的令牌凭据。

具体实现步骤

1. 安装依赖包

首先在C#项目中安装以下Nuget包:
Microsoft.Graph、Microsoft.Identity.Client、Microsoft.Identity.Client.Extensions.Msal

2. 初始化带持久缓存的公共客户端应用

using Microsoft.Identity.Client;
using Microsoft.Identity.Client.Extensions.Msal;
using Azure.Core;
using Microsoft.Graph;

// 配置参数
private const string ClientId = "你的应用注册客户端ID";
private const string TenantId = "organizations"; // 单租户场景可替换为实际租户ID
private static readonly string[] Scopes = { "Tasks.ReadWrite", "Group.Read.All" }; // 按需替换为实际需要的Graph权限

// 初始化PublicClientApplication
var pca = PublicClientApplicationBuilder
    .Create(ClientId)
    .WithAuthority($"https://login.microsoftonline.com/{TenantId}")
    .Build();

// 配置磁盘持久化缓存,适配Linux环境
var storageProperties = new StorageCreationPropertiesBuilder(
    "msal_cache.bin", // 缓存文件名
    MsalCacheHelper.UserRootDirectory) // Linux下默认路径为~/.local/share/MSAL/
    // 无头Linux服务器无桌面密钥环时,取消下一行注释使用明文存储(生产环境请严格限制文件权限)
    // .WithUnprotectedFile()
    .Build();

var cacheHelper = await MsalCacheHelper.CreateAsync(storageProperties);
cacheHelper.RegisterCache(pca.UserTokenCache);

3. 自定义适配Graph SDK的令牌凭据

实现TokenCredential抽象类,内部调用MSAL接口获取令牌:

public class MsalPublicClientCredential : TokenCredential
{
    private readonly IPublicClientApplication _pca;
    private readonly string[] _scopes;
    private readonly Func<DeviceCodeInfo, CancellationToken, Task> _deviceCodeCallback;

    public MsalPublicClientCredential(IPublicClientApplication pca, string[] scopes, Func<DeviceCodeInfo, CancellationToken, Task> deviceCodeCallback)
    {
        _pca = pca;
        _scopes = scopes;
        _deviceCodeCallback = deviceCodeCallback;
    }

    public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
    {
        // 优先尝试从缓存静默获取令牌
        var accounts = await _pca.GetAccountsAsync();
        var firstAccount = accounts.FirstOrDefault();
        AuthenticationResult authResult;
        try
        {
            authResult = await _pca.AcquireTokenSilent(_scopes, firstAccount)
                .ExecuteAsync(cancellationToken);
        }
        catch (MsalUiRequiredException)
        {
            // 缓存不存在/失效时走设备码验证流程
            authResult = await _pca.AcquireTokenWithDeviceCode(_scopes, _deviceCodeCallback)
                .ExecuteAsync(cancellationToken);
        }
        return new AccessToken(authResult.AccessToken, authResult.ExpiresOn);
    }

    public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
    {
        return GetTokenAsync(requestContext, cancellationToken).AsTask().GetAwaiter().GetResult();
    }
}

4. 初始化GraphServiceClient

// 设备码回调逻辑,可按需自定义输出方式
async Task DeviceCodeCallback(DeviceCodeInfo info, CancellationToken ct)
{
    Console.WriteLine(info.Message);
    await Task.CompletedTask;
}

// 创建自定义凭据
var tokenCredential = new MsalPublicClientCredential(pca, Scopes, DeviceCodeCallback);

// 初始化Graph客户端,后续直接调用即可
var graphClient = new GraphServiceClient(tokenCredential, Scopes);

Linux环境注意事项

  • 带桌面环境的Linux版本中,MSAL缓存默认会用系统密钥环加密存储,安全性更高。
  • 无头服务器无密钥环服务时,配置明文缓存后需要严格限制缓存文件的读写权限,仅允许运行进程的用户访问。
  • 刷新令牌默认有效期为90天,只要用户未主动撤销权限,90天内启动进程都无需重新验证,到期后会自动触发一次设备码流程更新凭证。

内容的提问来源于stack exchange,提问作者Luca Ottaviano

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 15:15:04