Cache de token em Microsoft.Identity.Web

A cache de tokens melhora o desempenho das aplicações, a fiabilidade e a experiência do utilizador. Microsoft. O Identity.Web oferece estratégias de cache flexíveis que equilibram desempenho, persistência e fiabilidade operacional.

Descrição geral

Esta secção descreve quais tokens a Microsoft.Identity.Web armazena em cache e explica por que o armazenamento em cache é importante para o seu aplicativo.

Que tokens são armazenados em cache?

Microsoft. O Identity.Web armazena em cache vários tipos de tokens:

Tipo de token Tamanho Scope Despejo
Tokens de acesso ~2 KB Per (utilizador/app, cliente, recurso) Automático (baseado em vida útil)
Tokens de Atualização Variable Por conta de utilizador Manual ou baseado em políticas
Fichas de identificação ~2-7 KB Por utilizador Automático

Onde se aplica a cache de tokens:

Por que armazenar tokens em cache?

Benefícios de Desempenho:

  • Reduz as viagens de ida e volta ao Microsoft Entra ID
  • Chamadas API mais rápidas (L1: <10ms vs L2: ~30ms vs rede: >100ms)
  • Menor latência para utilizadores finais

Benefícios de Fiabilidade:

  • Continua a trabalhar durante interrupções temporárias da Microsoft Entra
  • Resiliente a perturbações de rede
  • Degradação suave quando a cache distribuída falha

Custos-benefícios:

  • Reduz os pedidos de autenticação (evitação de limitação)
  • Custos mais baixos do Azure para operações de autenticação

Início Rápido

Comece rapidamente com uma das seguintes configurações de cache, dependendo do seu ambiente.

Desenvolvimento - cache em memória

O exemplo seguinte adiciona uma cache de tokens em memória, adequada para desenvolvimento e samples:

using Microsoft.Identity.Web;

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

Vantagens:

  • Configuração simples
  • Desempenho rápido
  • Sem dependências externas

Desvantagens:

  • Cache perdida ao reiniciar a aplicação. Numa aplicação web, os utilizadores mantêm-se iniciados através do cookie, mas têm de voltar a iniciar sessão para obter um token de acesso e repovoar a cache
  • Não adequado para implementações multi-servidor em produção
  • Não partilhado entre instâncias de aplicação

Produção - cache distribuída

Para aplicações de produção, especialmente implementações multi-servidor, utilize uma cache distribuída apoiada pelo Redis ou outro fornecedor:

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

Vantagens:

  • Sobrevive a reinícios da aplicação
  • Partilhado entre todas as instâncias de aplicação
  • Cache automática L1+L2

Desvantagens:

  • Requer infraestrutura de cache externa
  • Complexidade adicional de configuração
  • Latência de rede para operações de cache

Escolher uma estratégia de cache

Use o seguinte fluxograma de decisão e matriz para selecionar a estratégia de cache que melhor se adequa à sua implementação.

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

Matriz de decisão

A tabela seguinte resume os tipos de cache recomendados para cenários de implementação comuns.

Scenario Cache Recomendado Fundamentação
Desenvolvimento local In-Memory Simplicidade, sem necessidade de infraestrutura
Amostras/demonstrações In-Memory Configuração fácil para demonstrações
Ambiente de produção em servidor único (reinícios aceitáveis) In-Memory Aceitável se as sessões puderem ser restabelecidas
Produção multi servidor Redis Cache partilhada, alto desempenho, fiável
Aplicações hospedadas no Azure Cache do Azure para Redis Integração nativa com Azure, serviço gerido
Empresa on-premises SQL Server Aproveita a infraestrutura existente
Ambientes PostgreSQL PostgreSQL Utiliza a base de dados PostgreSQL existente, semântica SQL familiar
Ambientes de alta segurança SQL Server + Encriptação Residência de dados, encriptação em repouso
Testar cenários distribuídos Memória Distribuída Testa o comportamento da cache L2 sem infraestrutura

Implementações de cache

Microsoft. O Identity.Web suporta várias implementações de cache. Escolha aquele que corresponda às suas necessidades de infraestrutura e disponibilidade.

Cache na memória

Quando usar:

  • Desenvolvimento e testes
  • Implementações de servidor único com comportamento aceitável de reinício
  • Amostras e protótipos

Configuration:

O seguinte código regista a cache de tokens em memória com as definições predefinidas:

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

Com opções personalizadas:

Pode personalizar limites de validade e tamanho passando opções:

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

→ Saiba mais sobre a configuração da cache em memória


Cache distribuída (L2) com suporte automático a L1

Quando usar:

  • Implementações em produção de multi-servidores
  • Aplicações que requerem persistência de cache durante reinícios
  • Cenários de elevada disponibilidade

Característica chave: Desde Microsoft. Identity.Web v1.8.0, a cache distribuída inclui automaticamente uma cache L1 em memória para desempenho e fiabilidade.

Adicione a cadeia de conexão Redis ao appsettings.json:

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

Depois, regista a cache de tokens distribuídos e o fornecedor Redis em 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 do Azure para Redis

Para usar Cache do Azure para Redis, registe a cache com a sua cadeia de ligação Azure.

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

Formato da cadeia de ligação:

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

Cache do SQL Server

O exemplo seguinte configura o SQL Server como backend de cache distribuída:

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 Cache

O exemplo seguinte configura o Azure Cosmos DB como backend de cache distribuída:

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 cache

Requer o pacote 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
  }
}

Depois regista a cache PostgreSQL em 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);
});

→ Saiba mais sobre configuração de cache distribuída


Atenção

O cache baseado em sessões tem limitações significativas. Usa uma cache distribuída em vez disso.

O exemplo seguinte mostra a cache de tokens baseada em sessões para referência:

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:

  • Problemas com o tamanho das cookies - Tokens de identificação grandes com muitas declarações causam problemas
  • Conflitos de Âmbito - Não pode ser usado com um singleton TokenAcquisition (por exemplo, Microsoft Graph SDK)
  • Afinidade de sessão necessária - Não funciona bem em cenários com balanceamento de carga
  • Não recomendado - Use cache distribuído em vez disso

Configuração avançada

Estas opções permitem-lhe ajustar o comportamento da cache para desempenho, segurança e políticas de despejo.

Controlo de cache L1

A cache L1 (em memória) melhora o desempenho quando utiliza caches distribuídos. O seguinte código configura o tamanho e o comportamento da 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 desativar o L1:

  • Ausência de afinidade de sessão no balanceador de carga
  • Os utilizadores são solicitados frequentemente a usar MFA devido à inconsistência do cache do sistema.
  • Compensação: o acesso L2 é mais lento (~30ms em comparação com ~10ms)

Políticas de remoção de cache

As políticas de despejo controlam quando os tokens em cache são removidos. O seguinte código define a expiração absoluta e a deslizante:

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

Também pode configurar a expulsão via appsettings.json:

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

Recommendations:

  • Defina a expiração mais longa do que a vida útil dos tokens (os tokens normalmente expiram em 1 hora)
  • Padrão: 90 minutos de expiração deslizante
  • Equilíbrio entre o uso de memória e a experiência do utilizador
  • Considere: 72 horas absolutas + 2 horas ajustáveis para uma boa experiência do utilizador

→ Saiba mais sobre estratégias de despejo da cache


Encriptação em repouso

Para proteger dados sensíveis de tokens em caches distribuídos, ative a encriptação através do ASP.NET Core Data Protection.

Única máquina

Numa única máquina, ative a encriptação com o fornecedor de Proteção de Dados incorporado:

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

Sistemas distribuídos (múltiplos servidores)

Importante

Os sistemas distribuídos não partilham chaves de encriptação por defeito. Deve configurar a partilha de chaves:

Azure Key Vault (recomendado):

O código seguinte mantém as chaves do Armazenamento de Blobs do Azure e protege-as com o 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());

Baseado em certificados:

O seguinte código mantém as chaves de uma partilha de ficheiros e protege-as com um certificado 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"]));

→ Saiba mais sobre encriptação e proteção de dados


Considerações de desempenho da cache

Use as seguintes estimativas para planear a capacidade de cache para a sua aplicação.

Estimativas do tamanho do token

Tipo de token Tamanho Típico Per Notes
Tokens de aplicação ~2 KB Recurso × Inquilino Removido automaticamente
Tokens de utilizador ~7 KB Utilizador × Tenant × Recurso É necessário despejo manual
Atualizar tokens Variable User Longevidade

Planeamento da memória

Para 500 utilizadores simultâneos a chamar 3 APIs:

  • Tokens de utilizador: 500 × 3 × 7 KB = 10,5 MB
  • Com sobrecarga: ~15-20 MB

Para 10.000 utilizadores simultâneos:

  • Tokens de utilizador: 10.000 × 3 × 7 KB = 210 MB
  • Com custos fixos: ~300-350 MB

Recomendação: Defina o limite do tamanho da cache L1 com base nos utilizadores concorrentes esperados.

Melhores práticas

Siga estas orientações para garantir um cache de tokens fiável e eficiente.

Use cache distribuído em produção - Essencial para as implementações com múltiplos servidores

Defina limites de tamanho de cache apropriados - Evite o crescimento ilimitado da memória

Configurar políticas de despejo - Equilibrar a experiência de utilizador e o uso de memória

Ativar encriptação para dados sensíveis - Proteger os tokens em repouso

Monitorizar a saúde da cache - Acompanhar as taxas de acerto, falhas e desempenho

Trate as falhas do cache L2 com elegância - o cache L1 garante resiliência

Testar o comportamento da cache - Verificar cenários de reinício e failover

Não uses cache de memória distribuída em produção - Não é persistente nem distribuída

Não uses o cache de sessão - Tem limitações significativas

Não defina a expiração mais curta do que a vida útil do token - Força a reautenticação desnecessária

Não se esqueça da partilha de chaves de encriptação - Sistemas distribuídos precisam de chaves partilhadas