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

ASP.NET使用MSAL.NET对接Graph API时如何实现令牌持久化?

MSAL.NET SQL Server分布式令牌缓存不生效解决方案

你当前的写法误用了AddDistributedTokenCache扩展方法,该方法仅适配ASP.NET Core依赖注入(DI)注册的认证流程,手动构建的ConfidentialClientApplication实例无法识别该配置,因此仍默认使用内存缓存。可根据你的项目场景选择以下修改方案:


场景1:ASP.NET Core 项目(推荐)

直接走DI注册流程,不要手动构建客户端,步骤如下:

  • 先安装必要NuGet包:Microsoft.Identity.Web、Microsoft.Identity.Web.MicrosoftGraph、Microsoft.Extensions.Caching.SqlServer
  • 执行CLI命令创建缓存表(提前给连接字符串账号开放对应库的表创建权限):
    dotnet sql-cache create "你的SQL Server连接字符串" dbo TokenCache
  • 在Program.cs中注册服务:
// 注册认证与MSAL服务
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi(new[] { "你的Graph API权限Scope" })
    .AddDistributedTokenCaches();

// 注册SQL Server分布式缓存
builder.Services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = builder.Configuration.GetConnectionString("TokenCacheConn");
    options.SchemaName = "dbo";
    options.TableName = "TokenCache";
    options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});

后续直接通过DI获取ITokenAcquisition或者GraphServiceClient即可,令牌会自动写入SQL缓存。


场景2:必须手动构建ConfidentialClientApplication

手动对接分布式缓存的读写事件,代码如下:

// 先构建分布式缓存实例
var cacheServices = new ServiceCollection();
cacheServices.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = @"你的SQL连接字符串";
    options.SchemaName = "dbo";
    options.TableName = "TokenCache";
    options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});
var cacheProvider = cacheServices.BuildServiceProvider();
var distributedCache = cacheProvider.GetRequiredService<IDistributedCache>();

// 构建MSAL客户端
var App = ConfidentialClientApplicationBuilder.Create("客户端ID")
    .WithClientSecret("客户端密钥")
    .WithTenantId("租户ID")
    .WithRedirectUri("回调地址")
    .WithLegacyCacheCompatibility(false)
    .Build();

// 配置用户令牌缓存读写逻辑(用户委托流用)
App.UserTokenCache.SetBeforeAccessAsync(async args =>
{
    var cacheData = await distributedCache.GetAsync(args.SuggestedCacheKey, args.CancellationToken);
    if (cacheData != null)
    {
        args.TokenCache.DeserializeMsalV3(cacheData);
    }
});
App.UserTokenCache.SetAfterAccessAsync(async args =>
{
    if (args.HasStateChanged)
    {
        await distributedCache.SetAsync(
            args.SuggestedCacheKey,
            args.TokenCache.SerializeMsalV3(),
            new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(90) },
            args.CancellationToken);
    }
});

// 如果是客户端凭证流,还需要配置应用令牌缓存
App.AppTokenCache.SetBeforeAccessAsync(async args =>
{
    var cacheData = await distributedCache.GetAsync(args.SuggestedCacheKey, args.CancellationToken);
    if (cacheData != null)
    {
        args.TokenCache.DeserializeMsalV3(cacheData);
    }
});
App.AppTokenCache.SetAfterAccessAsync(async args =>
{
    if (args.HasStateChanged)
    {
        await distributedCache.SetAsync(
            args.SuggestedCacheKey,
            args.TokenCache.SerializeMsalV3(),
            new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(90) },
            args.CancellationToken);
    }
});

注意事项

  • 必须先成功调用一次AcquireToken系列方法获取到有效令牌,才会触发缓存写入,首次未获取到令牌时缓存表为空是正常现象
  • 确保SQL连接字符串有对应表的读写权限,避免写入失败
  • 不要修改默认的SuggestedCacheKey,MSAL已内置用户、租户、客户端等维度的隔离逻辑,避免缓存冲突

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 08:27:03