你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

MSAL.NET 中的令牌缓存序列化

Microsoft 身份验证库 (MSAL) 获取令牌后,会缓存该令牌。 在通过其他方法获取令牌之前,公共客户端应用程序(桌面和移动应用)应该先尝试从缓存中获取令牌。 机密客户端应用程序中的令牌获取方法可以自行管理缓存。 本文介绍 MSAL.NET 中令牌缓存的默认序列化和自定义序列化。

快速摘要

建议如下:

  • 在编写桌面应用程序时使用跨平台令牌缓存,如桌面应用中所述。
  • 不对移动应用和 UWP 应用执行任何操作。 MSAL.NET 为缓存提供安全存储。
  • 在 ASP.NET Core Web 应用Web API 中,使用 Microsoft.Identity.Web 作为更高级别的 API。 你将获得令牌缓存等内容。 请参阅 ASP.NET Core Web 应用和 Web API
  • 对于其他情况的 Web 应用Web API
    • 如果在生产应用程序中为用户请求令牌,请使用分布式令牌缓存(Redis、SQL Server、Azure Cosmos DB、分布式内存)。 使用 Microsoft.Identity.Web.TokenCache 中提供的令牌缓存序列化程序。
    • 否则,如果要使用内存中缓存:
      • 如果你只使用 AcquireTokenForClient,要么重用机密客户端应用程序实例,而不添加序列化程序,要么创建新的机密客户端应用程序并启用共享缓存选项

        共享缓存的速度更快,因为它没有被序列化。 不过,内存会随着令牌的缓存量而增长。 令牌数等于租户数乘以下游 API 数。 应用令牌的大小约为 2 KB,而用户的令牌大小约为 7 KB。 它适用于开发或者用户较少的情况。

      • 如果要使用内存中令牌缓存并控制其大小和逐出策略,请使用 Microsoft.Identity.Web 内存中缓存选项

  • 如果要生成 SDK,并且想要为机密客户端应用程序编写自己的令牌缓存序列化程序,请从 Microsoft.Identity.Web.MsalAbstractTokenCacheProvider 继承并重写 WriteCacheBytesAsyncReadCacheBytesAsync 方法。

Microsoft.Identity.Web.TokenCache NuGet 包在 Microsoft.Identity.Web 库中提供了令牌缓存序列化。

如果直接在 ASP.NET Core 应用中使用 MSAL 库,请考虑改用 Microsoft.Identity.Web,后者提供更简单、更高级别的 API。 如果未使用 MSAL 库,请参阅非 ASP.NET Core Web 应用和 Web API,其中介绍了直接 MSAL 用法。

扩展方法 说明
AddInMemoryTokenCaches 在内存中创建临时缓存用于令牌存储和检索。 内存中令牌缓存的速度比其他缓存类型更快,但其令牌不会在应用程序重启之间持续存在,并且你无法控制缓存大小。 内存中缓存适用于不需要在应用重启之间保留令牌的应用程序。 在参与计算机到计算机身份验证方案的应用中使用内存中令牌缓存,例如服务、守护程序和其他使用 AcquireTokenForClient(客户端凭据授予)的应用程序。 内存中令牌缓存还适用于示例应用程序和本地应用开发。 Microsoft.Identity.Web 版本 1.19.0+ 在所有应用程序实例之间共享内存中令牌缓存。
AddSessionTokenCaches 令牌缓存将绑定到用户会话。 如果 ID 令牌包含很多声明,此选项并不是理想的选择,因为 cookie 将会变得过大。
AddDistributedTokenCaches 令牌缓存是针对 ASP.NET Core IDistributedCache 实现的适配器。 使用它可以在分布式内存缓存、Redis 缓存、分布式 NCache 或 SQL Server 缓存之间进行选择。 有关 IDistributedCache 实现的详细信息,请参阅分布式内存缓存

内存中令牌缓存

下面是一个代码示例,演示了在 ASP.NET Core 应用程序中 Startup 类的 ConfigureServices 方法中使用内存中缓存:

#using Microsoft.Identity.Web
using Microsoft.Identity.Web;

public class Startup
{
 const string scopesToRequest = "user.read";
  
  public void ConfigureServices(IServiceCollection services)
  {
   // code before
   services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
           .AddMicrosoftIdentityWebApp(Configuration)
             .EnableTokenAcquisitionToCallDownstreamApi(new string[] { scopesToRequest })
                .AddInMemoryTokenCaches();
   // code after
  }
  // code after
}

如果请求仅限应用的令牌,则适合在生产环境中使用 AddInMemoryTokenCaches。 如果使用用户令牌,请考虑使用分布式令牌缓存。

ASP.NET Core Web 应用和 Web API 之间的令牌缓存配置代码类似。

分布式令牌缓存

下面是可能的分布式缓存示例:

// or use a distributed Token Cache by adding
   services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
           .AddMicrosoftIdentityWebApp(Configuration)
             .EnableTokenAcquisitionToCallDownstreamApi(new string[] { scopesToRequest }
               .AddDistributedTokenCaches();

// Distributed token caches have a L1/L2 mechanism.
// L1 is in memory, and L2 is the distributed cache
// implementation that you will choose below.
// You can configure them to limit the memory of the 
// L1 cache, encrypt, and set eviction policies.
services.Configure<MsalDistributedTokenCacheAdapterOptions>(options => 
  {
    // Optional: Disable the L1 cache in apps that don't use session affinity
    //                 by setting DisableL1Cache to 'true'.
    options.DisableL1Cache = false;
    
    // Or limit the memory (by default, this is 500 MB)
    options.L1CacheOptions.SizeLimit = 1024 * 1024 * 1024; // 1 GB

    // You can choose if you encrypt or not encrypt the cache
    options.Encrypt = false;

    // And you can set eviction policies for the distributed
    // cache.
    options.SlidingExpiration = TimeSpan.FromHours(1);
  });

// Then, choose your implementation of distributed cache
// -----------------------------------------------------

// good for prototyping and testing, but this is NOT persisted and it is NOT distributed - do not use in production
services.AddDistributedMemoryCache();

// Or a Redis cache
// Requires the Microsoft.Extensions.Caching.StackExchangeRedis NuGet package
services.AddStackExchangeRedisCache(options =>
{
 options.Configuration = "localhost";
 options.InstanceName = "SampleInstance";
});

// You can even decide if you want to repair the connection
// with Redis and retry on Redis failures. 
services.Configure<MsalDistributedTokenCacheAdapterOptions>(options => 
{
  options.OnL2CacheFailure = (ex) =>
  {
    if (ex is StackExchange.Redis.RedisConnectionException)
    {
      // action: try to reconnect or something
      return true; //try to do the cache operation again
    }
    return false;
  };
});

// Or even a SQL Server token cache
// Requires the Microsoft.Extensions.Caching.SqlServer NuGet package
services.AddDistributedSqlServerCache(options =>
{
 options.ConnectionString = _config["DistCache_ConnectionString"];
 options.SchemaName = "dbo";
 options.TableName = "TestCache";
});

// Or an Azure Cosmos DB cache
// Requires the Microsoft.Extensions.Caching.Cosmos NuGet package
services.AddCosmosCache((CosmosCacheOptions cacheOptions) =>
{
    cacheOptions.ContainerName = Configuration["CosmosCacheContainer"];
    cacheOptions.DatabaseName = Configuration["CosmosCacheDatabase"];
    cacheOptions.ClientBuilder = new CosmosClientBuilder(Configuration["CosmosConnectionString"]);
    cacheOptions.CreateIfNotExists = true;
});

有关详细信息,请参阅:

有关分布式缓存的使用,可查看阶段 2-2 令牌缓存中的 ASP.NET Core Web 应用教程

监视缓存命中率和缓存性能

MSAL 公开重要指标作为 AuthenticationResult.AuthenticationResultMetadata 对象的一部分。 可以记录这些指标以评估应用程序的运行状况。

指标 含义 何时触发警报?
DurationTotalInMs MSAL 中花费的总时间,包括网络调用和缓存。 针对整体高延迟(> 1 秒)的警报。 值取决于令牌源。 从缓存:一个缓存访问。 从 Azure Active Directory (Azure AD):两个缓存访问加一个 HTTP 调用。 第一次调用(每进程)需要更长的时间,因为有一个额外的 HTTP 调用。
DurationInCacheInMs 加载或保存令牌缓存所用的时间(由应用开发人员自定义)(例如,保存到 Redis)。 对高峰发出警报。
DurationInHttpInMs 向 Azure AD 发出 HTTP 调用所花的时间。 对高峰发出警报。
TokenSource 令牌的源。 从缓存中检索令牌的速度要快得多(例如,~100 ms 与 ~700 ms)。 可用于监视缓存命中率并发出警报。 DurationTotalInMs 结合使用。
CacheRefreshReason 从标识提供者提取访问令牌的原因。 TokenSource 结合使用。

后续步骤

以下示例演示了令牌缓存序列化。

示例 平台 说明
active-directory-dotnet-desktop-msgraph-v2 桌面 (WPF) 调用 Microsoft Graph API 的 Windows 桌面 .NET (WPF) 应用程序。 该图显示了一个拓扑,其中桌面应用客户端通过以交互方式获取令牌流向 Azure Active Directory,并流向 Microsoft Graph。
active-directory-dotnet-v1-to-v2 桌面(控制台) 一组 Visual Studio 解决方案,用于说明 Azure AD v1.0 应用程序(使用 ADAL.NET)到 Microsoft 标识平台应用程序(使用 MSAL.NET)的迁移。 具体而言,请参阅令牌缓存迁移机密客户端令牌缓存
ms-identity-aspnet-webapp-openidconnect ASP.NET (net472) ASP.NET MVC 应用程序中的令牌缓存序列化的示例(使用 MSAL.NET)。 具体而言,请参阅 MsalAppBuilder