Cache dei token in Microsoft.Identity.Web

La memorizzazione nella cache dei token migliora le prestazioni, l'affidabilità e l'esperienza utente dell'applicazione. Microsoft. Identity.Web offre strategie di memorizzazione nella cache flessibili che bilanciano le prestazioni, la persistenza e l'affidabilità operativa.

Informazioni generali

Questa sezione descrive i token Microsoft. Identity.Web caches e perché la memorizzazione nella cache è importante per l'applicazione.

Quali token vengono memorizzati nella cache?

Microsoft. Identity.Web memorizza nella cache diversi tipi di token:

Tipo di token Dimensione Ambito Sfratto
Token di accesso ~2 KB Per (utente/app, tenant, risorsa) Automatico (basato sulla durata)
Token di aggiornamento Variable Per account utente Manuale o basato su criteri
Token ID ~2-7 KB Per utente Automatico

Dove si applica la memorizzazione nella cache dei token:

Perché memorizzare nella cache i token?

Vantaggi delle prestazioni:

  • Riduce i round trip per Microsoft Entra ID
  • Chiamate API più veloci (L1: <10ms vs L2: ~30ms vs rete: >100ms)
  • Bassa latenza per gli utenti finali

Vantaggi dell'affidabilità:

  • Continua a funzionare durante le interruzioni temporanee di Microsoft Entra
  • Resiliente ai temporanei di rete
  • Degradazione graduale quando la cache distribuita fallisce

Vantaggi dei costi:

  • Riduce le richieste di autenticazione (limitazione della frequenza)
  • Costi di Azure inferiori per le operazioni di autenticazione

Quickstart

Iniziare rapidamente con una delle configurazioni della cache seguenti, a seconda dell'ambiente in uso.

Sviluppo - cache in memoria

L'esempio seguente aggiunge una cache dei token in memoria, adatta per lo sviluppo e gli esempi:

using Microsoft.Identity.Web;

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

Vantaggi:

  • Impostazione semplice
  • Prestazioni veloci
  • Nessuna dipendenza esterna

Svantaggi:

  • Cache persa al riavvio dell'app. In un'app Web gli utenti rimangono connessi tramite il cookie, ma devono eseguire di nuovo l'accesso per ottenere un token di accesso e ripopolare la cache
  • Non adatto per le distribuzioni multiserver di produzione
  • Non condiviso tra istanze dell'applicazione

Produzione - Cache distribuita

Per le applicazioni di produzione, in particolare le distribuzioni multiserver, usare una cache distribuita supportata da Redis o da un altro provider:

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_";
});

Vantaggi:

  • Sopravvive ai riavvii dell'app
  • Condiviso tra tutte le istanze dell'applicazione
  • Memorizzazione automatica nella cache L1+L2

Svantaggi:

  • Richiede l'infrastruttura della cache esterna
  • Complessità aggiuntiva della configurazione
  • Latenza di rete per le operazioni della cache

Scelta di una strategia di cache

Usare il diagramma di flusso decisionale e la matrice seguenti per selezionare la strategia di cache più adatta alla distribuzione.

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

Matrice decisionale

La tabella seguente riepiloga i tipi di cache consigliati per scenari di distribuzione comuni.

Scenario Cache consigliata Motivazione
Sviluppo locale In-Memory Semplicità, nessuna infrastruttura necessaria
Esempi/dimostrazioni In-Memory Configurazione semplice per le dimostrazioni
Produzione a server singolo (riavvii possibili) In-Memory Accettabile se le sessioni possono essere ristabilite
Produzione multiserver Redis Cache condivisa, prestazioni elevate, affidabile
applicazioni ospitate su Azure cache di Azure per Redis Integrazione Azure nativa, servizio gestito
Organizzazione locale SQL Server Sfrutta l'infrastruttura esistente
Ambienti PostgreSQL PostgreSQL Usa il database PostgreSQL esistente, semantica SQL familiare
Ambienti a sicurezza elevata SQL Server e crittografia Residenza dei dati, crittografia in stato di inattività
Test di scenari distribuiti Memoria distribuita Verifica il comportamento della cache L2 senza infrastruttura

Implementazioni della cache

Microsoft. Identity.Web supporta diverse implementazioni della cache. Scegliere quello che corrisponde ai requisiti di infrastruttura e disponibilità.

Cache di memoria

Quando usare:

  • Sviluppo e test
  • Distribuzioni a server singolo con un comportamento di riavvio accettabile
  • Esempi e prototipi

Configuration:

Il codice seguente registra la cache dei token in memoria con le impostazioni predefinite:

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

Con opzioni personalizzate:

È possibile personalizzare i limiti di scadenza e dimensioni passando le opzioni seguenti:

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
    });

→ Altre informazioni sulla configurazione della cache in memoria


Cache distribuita (L2) con supporto L1 automatico

Quando usare:

  • Distribuzioni multiserver di produzione
  • Applicazioni che richiedono la persistenza della cache tra riavvii
  • Scenari di disponibilità elevata

Caratteristica chiave: Da Microsoft.Identity.Web v1.8.0, la cache distribuita include automaticamente una cache L1 in memoria per prestazioni e affidabilità.

Aggiungere il stringa di connessione Redis a appsettings.json:

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

Registrare quindi la cache dei token distribuiti e il provider Redis in Program.cs:

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
    };
});

cache di Azure per Redis

Per usare cache di Azure per Redis, registrare la cache con il Azure stringa di connessione:

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

Formato della stringa di connessione:

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

cache di SQL Server

L'esempio seguente configura SQL Server come back-end della cache distribuita:

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);
});

cache Azure Cosmos DB

L'esempio seguente configura Azure Cosmos DB come back-end della cache distribuita:

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;
});

Cache PostgreSQL

Richiede il pacchetto NuGet Microsoft.Extensions.Caching.Postgres.

appsettings.json:

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

Registrare quindi la cache PostgreSQL in Program.cs:

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);
});

→ Altre informazioni sulla configurazione della cache distribuita


Attenzione

La memorizzazione nella cache basata su sessione presenta limitazioni significative. Usare invece una cache distribuita.

L'esempio seguente illustra la memorizzazione nella cache dei token basata su sessione per riferimento:

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:

  • Problemi di dimensioni dei cookie - Token ID di grandi dimensioni con molte attestazioni causano problemi
  • conflitti di ambito Scope - non è possibile utilizzare con singleton TokenAcquisition (ad esempio, Microsoft Graph SDK)
  • Affinità di sessione richiesta : non funziona correttamente negli scenari con carico bilanciato
  • Non consigliato : usare invece la cache distribuita

Configurazione avanzata

Queste opzioni consentono di ottimizzare il comportamento della cache per i criteri di prestazioni, sicurezza e rimozione.

Controllo cache L1

La cache L1 (in memoria) migliora le prestazioni quando si usano cache distribuite. Il codice seguente configura le dimensioni e il comportamento della cache 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;
});

Quando disabilitare L1:

  • Nessuna affinità di sessione nel servizio di bilanciamento del carico
  • Gli utenti hanno spesso richiesto l'autenticazione a più fattori a causa dell'incoerenza della cache
  • Compromesso prestazionale: l'accesso a L2 è più lento (~30 ms contro ~10 ms)

Criteri di rimozione della cache

I criteri di rimozione controllano quando vengono rimossi i token memorizzati nella cache. Il codice seguente imposta la scadenza assoluta e scorrevole:

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);
});

È anche possibile configurare la rimozione tramite appsettings.json:

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

Raccomandazioni:

  • Impostare la scadenza più lunga della durata del token (in genere i token scadono in 1 ora)
  • Impostazione predefinita: scadenza variabile di 90 minuti
  • Bilanciare l'utilizzo della memoria e l'esperienza utente
  • Si consideri: 72 ore assolute + 2 ore scorrevoli per un'esperienza utente valida

→ Altre informazioni sulle strategie di rimozione della cache


Crittografia di dati inattivi

Per proteggere i dati dei token sensibili nelle cache distribuite, abilitare la crittografia tramite ASP.NET Core protezione dei dati.

Computer singolo

In un singolo computer abilitare la crittografia con il provider di protezione dati predefinito:

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

Sistemi distribuiti (più server)

Importante

I sistemi distribuiti non condividono le chiavi di crittografia per impostazione predefinita. È necessario configurare la condivisione delle chiavi:

Azure Key Vault (scelta consigliata):

Il codice seguente rende persistenti le chiavi per Archiviazione BLOB di Azure e le protegge con 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());

Basato su certificati:

Il codice seguente rende persistenti le chiavi di una condivisione file e le protegge con un certificato 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"]));

→ Altre informazioni sulla crittografia e la protezione dei dati


Considerazioni sulle prestazioni della cache

Usare le stime seguenti per pianificare la capacità della cache per l'applicazione.

Stime delle dimensioni dei token

Tipo di token Dimensioni tipiche Per Note
Token dell'app ~2 KB Risorsa × tenant Rimosso automaticamente
Token utente ~7 KB Utente × Tenant × Risorsa Rimozione manuale necessaria
Token di aggiornamento Variable User Di lunga durata

Pianificazione della memoria

Per 500 utenti simultanei che chiamano 3 API:

  • Token utente: 500 × 3 × 7 KB = 10,5 MB
  • Con sovraccarico: ~15-20 MB

Per 10.000 utenti simultanei:

  • Token utente: 10.000 × 3 × 7 KB = 210 MB
  • Con sovraccarico: ~300-350 MB

Raccomandazione: Impostare il limite di dimensioni della cache L1 in base agli utenti simultanei previsti.

Procedure consigliate

Seguire queste linee guida per garantire la memorizzazione nella cache dei token affidabile ed efficiente.

Usare la cache distribuita nell'ambiente di produzione - Essenziale per le distribuzioni multiserver

Impostare i limiti di dimensioni della cache appropriati - Impedire l'aumento non limitato della memoria

Configurare i criteri di rimozione - Bilanciare l'esperienza utente e l'utilizzo della memoria

Abilitare la crittografia per i dati sensibili - Proteggere i token a riposo

Monitorare l'integrità della cache - Tenere traccia delle percentuali di riscontri, degli errori e delle prestazioni

Gestire gli errori della cache L2 normalmente : la cache L1 garantisce la resilienza

Comportamento della cache di test - Verificare gli scenari di riavvio e il failover

Non usare la cache di memoria distribuita nell'ambiente di produzione : non persistente o distribuita

Non usare la cache delle sessioni : presenta limitazioni significative

Non impostare la scadenza più breve della durata del token - Forza l'autenticazione non necessaria

Non dimenticare la condivisione delle chiavi di crittografia - I sistemi distribuiti necessitano di chiavi condivise