在 Microsoft 驗證資源庫(MSAL)取得一個令牌後,會快取該令牌。 公用用戶端應用程式(桌面和行動應用程式)應先嘗試從快取取得權杖,再透過其他方法取得權杖。 機密客戶端應用程式的擷取方法會自行管理快取。 本文討論了 MSAL.NET 中憑證快取的預設與自訂序列化。
總結
建議如下:
- 在撰寫行動應用程式時,快取已經由 MSAL 預先設定好。
- 撰寫桌面應用程式時,請依桌面 應用程式中所述使用跨平台令牌快取。
- 在撰寫新的機密用戶端應用程式(網頁應用程式、網頁 API,或服務對服務或守護程序應用程式)時,請使用 Microsoft。Identity.Web 作為一個較高層次的 API。 它提供與 ASP.NET Core、ASP.NET Classic 的整合,也能獨立運作。
- 現有直接利用 MSAL.NET 的機密用戶端應用程式仍可繼續使用此功能。
- 網頁應用程式和網頁 API 應該使用分散式令牌快取(例如 Redis、SQL Server、Azure Cosmos DB)搭配受限的記憶體快取。
- 靜態加密可選擇性地使用 ASP.NET Core 資料保護來設定。
- 網頁應用程式也可能依賴工作階段 Cookie;不過,由於 Cookie 的大小限制,不建議使用此選項。
- 服務對服務應用程式和精靈應用程式可能僅依賴記憶體快取。 如果你的應用程式服務多個租戶,請設定驅逐政策。
- 管理式身份憑證僅快取於記憶體中。
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。
|
| 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)。 |