令牌快取提升應用程式效能、可靠性與使用者體驗。 Microsoft。Identity.Web 提供靈活的快取策略,平衡效能、持久性與營運可靠性。
概觀
本節說明 Microsoft.Identity.Web 快取的代幣以及快取對你的應用程式為何重要。
哪些代幣被快取?
Microsoft。Identity.Web 快取了幾種類型的標記:
| 記號類型 | Size | Scope | 驅逐 |
|---|---|---|---|
| 存取權杖 | ~2 KB | 每(使用者/應用程式/租戶/資源) | 自動(終身計算) |
| 刷新代幣 | 變數 | 每個使用者帳號 | 手動或策略導向 |
| ID 代幣 | ~2-7 KB | 每位使用者 | 自動 |
在何處使用代幣快取:
- 網頁應用程式呼叫 API- 用於委派存取的使用者憑證
- Web API 呼叫下游 API - OBO 代幣(需要謹慎的淘汰策略)
- 守護程序應用程式 - 僅用於服務間呼叫的應用程式令牌
為什麼要用快取代幣?
性能提升:
- 減少往返 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 快取(推薦)
將 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 快取 - 有明顯的限制
不要將到期時間設為短於代幣壽命 ——會強制不必要的重新認證
別忘了加密金鑰共享 ——分散式系統需要共享金鑰