標記快取序列化

在 Microsoft 驗證資源庫(MSAL)取得一個令牌後,會快取該令牌。 公用用戶端應用程式(桌面和行動應用程式)應先嘗試從快取取得權杖,再透過其他方法取得權杖。 機密客戶端應用程式的擷取方法會自行管理快取。 本文討論了 MSAL.NET 中憑證快取的預設與自訂序列化。

總結

建議如下:

Microsoft.Identity.Web.TokenCache NuGet 套件在 Microsoft.Identity.Web 程式庫中提供權杖快取序列化功能。 該函式庫可整合 ASP.NET Core 與 ASP.NET Classic,其抽象功能可用於驅動其他網頁應用程式或 API 框架。

Note

以下範例是針對 ASP.NET Core。 ASP.NET 的程式碼類似,參考ms-identity-aspnet-wepapp-openidconnect網頁應用範例作為參考實作。

擴展方式 Description
AddInMemoryTokenCaches 在記憶體中建立臨時快取,用於代幣儲存與檢索。 記憶體內的 token 快取比其他快取類型快取快,但它們的 token 不會在應用程式重啟之間持續存在,且你無法控制快取大小。 記憶體內快取適合不需要在應用程式重啟間持續存在的令牌的應用程式。 在參與機器對機器驗證情境的應用程式中,使用記憶體中的令牌快取,例如服務、守護程序及其他使用 AcquireTokenForClient (客戶端憑證授予)的應用程式。 記憶體內的標記快取也適用於範例應用程式及本地應用程式開發。 Microsoft。Identity.Web 版本 1.19.0+ 在所有應用程式實例間共享記憶體內的標記快取。
AddSessionTokenCaches 憑證快取綁定在使用者會話中。 如果 ID 代幣包含許多權利要求,這個選項並不理想,因為 cookie 會變得過大。
AddDistributedTokenCaches 權杖快取是 ASP.NET Core IDistributedCache 實作的配接器。 它讓你可以選擇分散式記憶體快取、Redis 快取、分散式 NCache 或 SQL Server 快取。 關於實 IDistributedCache 作的詳細資訊,請參見 分散式記憶體快取。

記憶體內標記快取

以下是一個在 ASP.NET Core 應用程式中啟動類別的ConfigureServices 方法中使用記憶體快取的程式碼範例:

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 網頁應用程式與網頁 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;
});

欲了解更多資訊,請參閱:

分散式快取的使用在 ASP.NET Core 網頁應用程式教學中,於第 2-2 階段令牌快取中介紹。

監控快取命中率與快取效能

MSAL 會作為 AuthenticationResult.AuthenticationResultMetadata 物件的一部分,揭露重要的指標。 你可以記錄這些指標,以評估申請的健康狀況。

Metric Meaning 什麼時候該觸發警報?
DurationTotalInMs 在 MSAL 中花費的總時間,包括網路呼叫和快取。 整體延遲過高時發出警報(> 1 秒)。 價值取決於代幣來源。 從快取中:一次快取存取。 從 Microsoft Entra ID 看:兩次快取存取加上一次 HTTP 呼叫。 第一次呼叫(每個程序)因為多了一個 HTTP 呼叫,需要更久的時間。
DurationInCacheInMs 花在載入或儲存權杖快取上的時間;權杖快取可由應用程式開發者自訂(例如儲存至 Redis)。 尖峰時發出警報。
DurationInHttpInMs 花在對 Microsoft Entra ID 發出 HTTP 呼叫上的時間。 尖峰時發出警報。
TokenSource 代幣的來源。 從快取中取出標記的速度快得多(例如,~100 毫秒對比 ~700 毫秒)。 可用於監控快取命中率並發出警報。 與 DurationTotalInMs 搭配使用。
CacheRefreshReason 從身份提供者取得存取權杖的原因。 與 TokenSource 搭配使用。

尺寸近似

使用代幣快取時,特別是對於高度可用且分散的應用程式,考慮快取的潛在大小非常重要。 當使用者登入時,會為每位使用者建立一筆快取項目,大小約為 7KB。 如果你同時呼叫多個下游 API,容量會更大。 對於服務對服務的認證,每個租戶和下游 API 都會有一個快取項目,大小約為 2KB。

詳細估算如下。

應用程式流程(AcquireTokenForClient, AcquireTokenForManagedIdentity)

  • 只有存取權杖會被快取。 一個標記,當持續存在時大約 2-3KB。 每個應用程式會有 1 個令牌,客戶 ID * 租戶數 * 下游資源。 例如,一個多租戶應用程式服務 1000 個租戶,且需要 Graph 和 SharePoint 的令牌,會使用:3KB * 1000 * 2,約 6 MB。

網站呼叫下游網頁 API(AcquireTokenByAuthCode)

  • 存取權杖 – 4 KB;每個 應用程式用戶端 ID * 使用者 * 租用戶 * 下游資源各 1 個權杖。
  • 刷新令牌 – 2KB;每個 客戶端應用程式ID 乘以使用者 1 個令牌。
  • ID 令牌 – 2KB;每個 客戶端應用程式 ID 1 個令牌 * 使用者 * 該使用者登入的租戶數量。

Note

我們強烈建議使用較 Microsoft.Identity.Web 高層級的 API,而非直接使用 MSAL。 快取的考量是一樣的。

Web API 呼叫其他 Web API(AcquireTokenOnBehalfOf)

與網站情境相同,但每個工作階段各有一個節點,而不是每位使用者各有一個節點。 預設情況下,MSAL 會透過雜湊上游斷言來識別會話,但這點可以被更改。 詳見 長時間執行的 OBO 程序。

Note

我們強烈建議使用較 Microsoft.Identity.Web 高層級的 API,而非直接使用 MSAL。 快取的考量是一樣的。

令牌快取類型

MSAL.NET 採用兩種類型的權杖快取:使用者和應用程式。

應用程式 令牌快取 ,用於存放此應用程式的存取權杖。 當呼叫 AcquireTokenForClient 時,它會被靜默維護和更新。

使用者令牌快取包含 ID 令牌、存取令牌及 MSAL.NET 互動帳號的刷新令牌。 當呼叫 AcquireTokenSilent 時,系統會在需要時以靜默方式使用並更新它。 它會依照每個令牌取得方法更新,唯獨 AcquireTokenForClient 僅使用應用程式快取。

下一步

以下範例說明了標記快取的序列化。

Sample Platform Description
Active-Directory-dotnet-desktop-msgraph-v2 桌面(WPF) Windows Desktop .NET(WPF)應用程式,呼叫 Microsoft 圖形 API。 圖示顯示一個拓撲結構,桌面應用程式用戶端透過互動式取得令牌流向 Microsoft Entra ID,並連接到 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)。