Microsoft.Identity.Web 中的 token 快取

令牌快取提升應用程式效能、可靠性與使用者體驗。 Microsoft。Identity.Web 提供靈活的快取策略,平衡效能、持久性與營運可靠性。

概觀

本節說明 Microsoft.Identity.Web 快取的代幣以及快取對你的應用程式為何重要。

哪些代幣被快取?

Microsoft。Identity.Web 快取了幾種類型的標記:

記號類型 Size Scope 驅逐
存取權杖 ~2 KB 每(使用者/應用程式/租戶/資源) 自動(終身計算)
刷新代幣 變數 每個使用者帳號 手動或策略導向
ID 代幣 ~2-7 KB 每位使用者 自動

在何處使用代幣快取:

為什麼要用快取代幣?

性能提升:

  • 減少往返 Microsoft Entra ID 的次數
  • 更快的 API 呼叫(L1:<10ms 比 L2:~30ms 比 網路:>100ms)
  • 降低終端用戶的延遲

可靠性優勢:

  • 在 Microsoft Entra 暫時停機期間仍能正常運作
  • 對網路瞬態的韌性
  • 分散式緩衝失效時的優雅降級

成本效益:

  • 減少認證請求(避免流量限制)
  • 降低 Azure 認證操作的成本

快速入門

請根據你的環境,立即著手選擇下列快取配置之一。

開發 - 記憶體內快取

以下範例新增了記憶體內的標記快取,適合開發與取樣:

using Microsoft.Identity.Web;

builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

優點:

  • 簡單設定
  • 快速性能
  • 沒有外部相依性

缺點:

  • 應用程式重啟時快取遺失。 在網頁應用程式中,使用者透過 cookie 保持登入狀態,但必須重新登入才能取得存取權杖並重新填充快取
  • 不適合生產多伺服器部署
  • 不會在應用程式實例間共享

生產 - 分散式快取

對於生產應用,尤其是多伺服器部署,請使用由 Redis 或其他提供者支援的分散式快取:

using Microsoft.Identity.Web;

builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDistributedTokenCaches();

// Choose your cache implementation
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("Redis");
    options.InstanceName = "MyApp_";
});

優點:

  • 應用程式重新啟動後仍然保持運行
  • 在所有應用程式實例間共享
  • 自動 L1+L2 快取

缺點:

  • 需要外部快取基礎設施
  • 額外的配置複雜度
  • 快取操作的網路延遲

選擇快取策略

請使用以下決策流程圖與矩陣,選擇最適合您部署的快取策略。

flowchart TD
    Start([Token Caching<br/>Decision]) --> Q1{Production<br/>Environment?}

    Q1 -->|No - Dev/Test| DevChoice[In-Memory Cache<br/>AddInMemoryTokenCaches]
    Q1 -->|Yes| Q2{Multiple Server<br/>Instances?}

    Q2 -->|No - Single Server| Q3{App Restarts<br/>Acceptable?}
    Q3 -->|Yes| DevChoice
    Q3 -->|No| DistChoice

    Q2 -->|Yes| DistChoice[Distributed Cache<br/>AddDistributedTokenCaches]

    DistChoice --> Q4{Cache<br/>Implementation?}

    Q4 -->|High Performance| Redis[Redis Cache<br/>StackExchange.Redis<br/>⭐ Recommended]
    Q4 -->|Azure Native| Azure[Azure Cache for Redis,<br/>Azure Cosmos DB,<br/>or Azure Database for PostgreSQL]
    Q4 -->|On-Premises| SQL[SQL Server Cache<br/>AddDistributedSqlServerCache]
    Q4 -->|Testing| DistMem[Distributed Memory<br/>Not for production]

    Redis --> L1L2[Automatic L1+L2<br/>Caching]
    Azure --> L1L2
    SQL --> L1L2
    DistMem --> L1L2

    L1L2 --> Config[Configure Options<br/>MsalDistributedTokenCacheAdapterOptions]
    DevChoice --> MemConfig[Configure Memory Options<br/>MsalMemoryTokenCacheOptions]

    style Start fill:#e1f5ff
    style DevChoice fill:#d4edda
    style DistChoice fill:#fff3cd
    style Redis fill:#d1ecf1
    style L1L2 fill:#f8d7da

決策矩陣

下表總結了常見部署情境下的推薦快取類型。

Scenario 推薦快取 理由
本機開發 In-Memory 簡單,不需要基礎設施
取樣/示範 In-Memory 示範設置簡便
單一伺服器生產(重新啟動正常) In-Memory 若能重新建立會話,則可接受
多伺服器生產 Redis 共享快取、高效能且可靠
Azure 託管應用程式 Azure Cache for Redis 本地 Azure 整合,托管服務
本地企業 SQL Server 善用現有基礎設施
PostgreSQL 環境 PostgreSQL 使用現有的 PostgreSQL 資料庫,熟悉的 SQL 語意
高安全性環境 SQL Server + 加密 資料駐留、靜態加密
分散式情境的測試 分散式記憶體 測試無基礎設施的 L2 快取行為

快取實作

Microsoft。Identity.Web 支援多種快取實作。 選擇符合你基礎設施和可用性需求的方案。

記憶體內部快取

使用時機:

  • 開發與測試
  • 具備可接受重啟行為的單伺服器部署
  • 樣本與原型

Configuration:

以下程式碼以預設設定註冊記憶體中的標記快取:

builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

有自訂選項:

你可以透過傳遞選項來自訂有效期和容量限制:

builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches(options =>
    {
        // Token cache entry will expire after this duration
        options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);

        // Limit cache size (default is unlimited)
        options.SizeLimit = 500 * 1024 * 1024; // 500 MB
    });

→ 了解更多關於記憶體內快取配置的資訊


分散式快取(L2)並自動支援 L1

使用時機:

  • 生產環境的多伺服器部署
  • 需要在重啟後保持快取持久性的應用程式
  • 高可用性場景

關鍵特性:自 Microsoft.Identity.Web v1.8.0 起,分散式快取會自動包含記憶體中的 L1 快取以提升效能與可靠性。

將 Redis 連接字串 加入 appsettings.json:

{
  "ConnectionStrings": {
    "Redis": "localhost:6379"
  }
}

接著在 Program.cs註冊分散式令牌快取與 Redis 提供者:

using Microsoft.Identity.Web;
using Microsoft.Identity.Web.TokenCacheProviders.Distributed;

builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDistributedTokenCaches();

// Redis cache implementation
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("Redis");
    options.InstanceName = "MyApp_"; // Unique prefix per application
});

// Optional: Configure distributed cache behavior
builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    // Control L1 cache size
    options.L1CacheOptions.SizeLimit = 500 * 1024 * 1024; // 500 MB

    // Handle L2 cache failures gracefully
    options.OnL2CacheFailure = (exception) =>
    {
        if (exception is StackExchange.Redis.RedisConnectionException)
        {
            // Log the failure
            // Optionally attempt reconnection
            return true; // Retry the operation
        }
        return false; // Don't retry
    };
});

Azure Cache for Redis

要使用 Azure Cache for Redis,請用您的 Azure 連線字串註冊快取:

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("AzureRedis");
    options.InstanceName = "MyApp_";
});

連接字串格式:

<cache-name>.redis.cache.windows.net:6380,password=<access-key>,ssl=True,abortConnect=False

SQL Server 快取

以下範例將 SQL Server 配置為分散式快取後端:

builder.Services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = builder.Configuration.GetConnectionString("TokenCacheDb");
    options.SchemaName = "dbo";
    options.TableName = "TokenCache";

    // Set expiration longer than access token lifetime (default 1 hour)
    // This prevents cache entries from expiring before tokens
    options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});

Azure Cosmos DB 快取

以下範例將 Azure Cosmos DB 配置為分散式快取後端:

builder.Services.AddCosmosCache((CosmosCacheOptions options) =>
{
    options.ContainerName = builder.Configuration["CosmosCache:ContainerName"];
    options.DatabaseName = builder.Configuration["CosmosCache:DatabaseName"];
    options.ClientBuilder = new CosmosClientBuilder(
        builder.Configuration["CosmosCache:ConnectionString"]);
    options.CreateIfNotExists = true;
});

PostgreSQL 快取

需要 Microsoft.Extensions.Caching.Postgres NuGet 套件。

appsettings.json:

{
  "ConnectionStrings": {
    "PostgresCache": "Host=localhost;Database=mydb;Username=myuser;Password=mypassword"
  },
  "PostgresCache": {
    "SchemaName": "public",
    "TableName": "token_cache",
    "CreateIfNotExists": true
  }
}

接著在 Program.cs 註冊 PostgreSQL 快取:

builder.Services.AddDistributedPostgresCache(options =>
{
    options.ConnectionString = builder.Configuration.GetConnectionString("PostgresCache");
    options.SchemaName = builder.Configuration["PostgresCache:SchemaName"];
    options.TableName = builder.Configuration["PostgresCache:TableName"];
    options.CreateIfNotExists = builder.Configuration.GetValue<bool>("PostgresCache:CreateIfNotExists");
    options.DefaultSlidingExpiration = TimeSpan.FromMinutes(90);
});

→ 了解更多關於分散式快取配置的資訊


謹慎

基於會話的快取有顯著的限制。 改用分散式快取。

以下範例展示了基於會話的令牌快取供參考:

using Microsoft.Identity.Web.TokenCacheProviders.Session;

// In Program.cs
builder.Services.AddSession();

builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddSessionTokenCaches();

// In middleware pipeline
app.UseSession(); // Must be before UseAuthentication()
app.UseAuthentication();
app.UseAuthorization();

Limitations:

  • Cookie 大小問題 - 大型 ID 標籤與多項聲明會造成問題
  • Scope 衝突 - 無法與單例 TokenAcquisition(例如 Microsoft Graph SDK )一起使用
  • 需要會話親和力 ——在負載平衡的情境下效果不佳
  • 不建議 ——改用分散式快取

進階設定

這些選項讓你能微調快取行為以調整效能、安全性及驅逐政策。

L1 快取控制

使用分散式快取時,L1(記憶體內快取)能提升效能。 以下程式碼用以配置 L1 快取大小與行為:

builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    // Control L1 cache size (default: 500 MB)
    options.L1CacheOptions.SizeLimit = 100 * 1024 * 1024; // 100 MB

    // Disable L1 cache if session affinity is not available
    // (forces all requests to use L2 cache for consistency)
    options.DisableL1Cache = false;
});

何時關閉 L1:

  • 負載平衡器中沒有會話黏性
  • 使用者經常因快取不一致而被要求多重驗證
  • 代價是:L2 存取較慢(約 30ms 對比 ~10ms)

快取淘汰策略

驅逐政策控制何時移除快取的代幣。 以下程式碼設定絕對到期日與滑動到期日:

builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    // Absolute expiration (removed after this time, regardless of use)
    options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(72);

    // Sliding expiration (renewed on each access)
    options.SlidingExpiration = TimeSpan.FromHours(2);
});

你也可以透過 appsettings.json設定驅逐:

{
  "TokenCacheOptions": {
    "AbsoluteExpirationRelativeToNow": "72:00:00",
    "SlidingExpiration": "02:00:00"
  }
}
builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(
    builder.Configuration.GetSection("TokenCacheOptions"));

建議:

  • 設定 過期時間超過代幣壽命 (代幣通常在1小時內過期)
  • 預設:90分鐘滑動到期
  • 記憶體使用與使用者體驗之間的平衡
  • 考慮一下:72小時絕對時間 + 2小時滑動時間以獲得良好的使用者體驗

→ 進一步了解快取清除策略


待用加密

為了保護分散式快取中的敏感令牌資料,請啟用 ASP.NET Core 資料保護加密功能。

單一電腦

在單一機器上,啟用內建的資料保護服務提供者加密:

builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
    options.Encrypt = true; // Uses ASP.NET Core Data Protection
});

分散式系統(多台伺服器)

這很重要

分散式系統預設 不會 共享加密金鑰。 您必須設定金鑰共享:

Azure Key Vault(推薦):

以下程式碼會持久化 Azure Blob 儲存體 的金鑰,並用 Azure Key Vault 保護它們:

using Microsoft.AspNetCore.DataProtection;

builder.Services.AddDataProtection()
    .PersistKeysToAzureBlobStorage(new Uri(builder.Configuration["DataProtection:BlobUri"]))
    .ProtectKeysWithAzureKeyVault(
        new Uri(builder.Configuration["DataProtection:KeyIdentifier"]),
        new DefaultAzureCredential());

證書制:

以下程式碼將檔案分享的金鑰持久化,並以 X.509 憑證保護:

builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\keys"))
    .ProtectKeysWithCertificate(
        new X509Certificate2("current.pfx", builder.Configuration["CertPassword"]))
    .UnprotectKeysWithAnyCertificate(
        new X509Certificate2("current.pfx", builder.Configuration["CertPassword"]),
        new X509Certificate2("previous.pfx", builder.Configuration["PrevCertPassword"]));

→ 了解更多關於加密與資料保護的資訊


快取效能考量

請利用下列的估算來規劃您的應用程式的快取容量。

代幣大小估計

記號類型 典型尺寸 佩爾 Notes
應用程式代幣 ~2 KB 租戶×資源 自動淘汰
使用者代幣 ~7 KB 使用者×租戶×資源 需要手動驅逐
重新整理令牌 變數 User 長壽

記憶規劃

針對 500 位同時呼叫的使用者,呼叫 3 個 API:

  • 用戶代幣:500 × 3 × 7 KB = 10.5 MB
  • 含開銷: ~15-20 MB

同時線上用戶 10,000 名:

  • 用戶代幣:10,000 個× 3 × 7 KB = 210 MB
  • 含開銷: ~300-350 MB

推薦: 根據預期的同時在線使用者數量設定 L1 快取大小限制。

最佳做法

請遵循這些指引,以確保代幣快取的可靠與效率。

在生產環境中使用分散式快取 ——多伺服器部署不可或缺

設定適當的快取大小限制 - 防止無限制的記憶體成長

設定驅逐政策 - 平衡使用者體驗與記憶體使用量

啟用敏感資料加密 - 保護靜置中的令牌

監控快取狀態 - 追蹤命中率、故障率與效能

優雅地處理 L2 快取故障-L1 快取確保彈性

測試快取行為 - 驗證重啟情境與故障轉移

不要在生產環境中使用分散式記憶體快取 ——不是持久式或分散式

不要使用 session 快取 - 有明顯的限制

不要將到期時間設為短於代幣壽命 ——會強制不必要的重新認證

別忘了加密金鑰共享 ——分散式系統需要共享金鑰