Cache de token em Microsoft.Identity.Web

O cache de tokens melhora o desempenho do aplicativo, a confiabilidade e a experiência do usuário. Microsoft. O Identity.Web fornece estratégias de cache flexíveis que equilibram o desempenho, a persistência e a confiabilidade operacional.

Visão geral

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

Quais tokens são armazenados em cache?

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

Tipo de token Tamanho Scope Despejo
Tokens de acesso ~2 KB Por (usuário/aplicativo, locatário, recurso) Automático (baseado em tempo de vida)
Atualizar tokens Variable Por conta de usuário Manual ou baseado em política
ID Tokens ~2-7 KB Por usuário Automático

Onde o cache de token se aplica:

Por que armazenar tokens em cache?

Benefícios de desempenho:

  • Reduz as viagens de ida e volta para Microsoft Entra ID
  • Chamadas de API mais rápidas (L1: <10ms vs L2: ~30ms versus rede: >100ms)
  • Latência mais baixa para usuários finais

Benefícios de confiabilidade:

  • Continua trabalhando durante interrupções temporárias do Microsoft Entra
  • Resiliente a transitórios de rede
  • Degradação normal quando o cache distribuído falha

Benefícios de custo:

  • Reduz solicitações de autenticação (prevenção de limitação)
  • Reduzir os custos de 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 a seguir adiciona um cache de token na memória, adequado para desenvolvimento e exemplos:

using Microsoft.Identity.Web;

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

Vantagens:

  • Configuração simples
  • Desempenho rápido
  • Nenhuma dependência externa

Desvantagens:

  • Cache perdido na reinicialização do aplicativo. Em um aplicativo Web, os usuários permanecem conectados por meio do cookie, mas devem entrar novamente para obter um token de acesso e preencher novamente o cache
  • Não adequado para implantações de vários servidores de produção
  • Não compartilhado entre instâncias de aplicativo

Produção – cache distribuído

Para aplicativos de produção, especialmente implantações de vários servidores, use um cache distribuído apoiado pelo Redis ou outro provedor:

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 às reinicializações do aplicativo
  • Compartilhado em todas as instâncias do aplicativo
  • Cache L1+L2 automático

Desvantagens:

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

Escolhendo uma estratégia de cache

Use o fluxograma de decisão e a matriz a seguir para selecionar a estratégia de cache que melhor se ajusta à sua implantaçã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 a seguir resume os tipos de cache recomendados para cenários comuns de implantação.

Scenario Cache recomendado Lógica
Desenvolvimento local In-Memory Simplicidade, nenhuma infraestrutura necessária
Exemplos/demonstrações In-Memory Configuração fácil para demonstrações
Produção de servidor único (reinicia OK) In-Memory Aceitável se as sessões puderem ser restabelecidas
Produção de vários servidores Redis Cache compartilhado, alto desempenho, confiável
aplicativos hospedados Azure Cache do Azure para Redis Integração de Azure nativa, serviço gerenciado
Empresa local SQL Server Aproveita a infraestrutura existente
Os ambientes PostgreSQL PostgreSQL Usa o banco de dados PostgreSQL existente, semântica familiar de SQL
Ambientes de alta segurança SQL Server + Criptografia Residência de dados, criptografia em repouso
Testando cenários distribuídos Memória Distribuída Testa o comportamento do cache L2 sem infraestrutura

Implementações de cache

Microsoft. O Identity.Web dá suporte a várias implementações de cache. Escolha aquele que corresponda aos requisitos de infraestrutura e disponibilidade.

Cache na memória

Quando usar:

  • Desenvolvimento e teste
  • Implantações de servidor único com comportamento de reinicialização aceitável
  • Exemplos e protótipos

Configuration:

O código a seguir registra o cache de token na memória com configurações padrão:

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

Com opções personalizadas:

Você pode personalizar os limites de expiração 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 de cache na memória


Cache distribuído (L2) com suporte automático de L1

Quando usar:

  • Implantações de vários servidores de produção
  • Aplicativos que exigem persistência de cache entre reinicializações
  • Cenários de alta disponibilidade

Key feature: Desde Microsoft. Identity.Web v1.8.0, o cache distribuído inclui automaticamente um cache L1 na memória para desempenho e confiabilidade.

Adicione a string de conexão Redis a appsettings.json:

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

Em seguida, registre o cache de token distribuído e o provedor Redis no 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 o Cache do Azure para Redis, registre o cache com a sua string de conexão do Azure.

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

Formato da cadeia de conexão:

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

cache SQL Server

O exemplo a seguir configura SQL Server como o back-end do cache distribuído:

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 do Azure Cosmos DB

O exemplo a seguir configura Azure Cosmos DB como o back-end do cache distribuído:

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 do PostgreSQL

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

Em seguida, registre o cache PostgreSQL no 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 a configuração de cache distribuído


Cuidado

O cache baseado em sessão tem limitações significativas. Em vez disso, use um cache distribuído.

O exemplo a seguir mostra o cache de token baseado em sessão 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();

Limitações:

  • Problemas de tamanho de cookie – Tokens de ID grandes com muitas declarações causam problemas
  • conflitos Scope - Não é possível usar com 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 o cache distribuído em vez disso

Configuração avançada

Essas opções permitem ajustar o comportamento do cache para políticas de desempenho, segurança e remoção.

Controle de cache L1

O cache L1 (na memória) melhora o desempenho quando você usa caches distribuídos. O código a seguir configura o tamanho e o comportamento do 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 desabilitar L1:

  • Não há afinidade de sessão no balanceador de carga
  • Usuários frequentemente solicitados a usar MFA por inconsistência no cache
  • Compensação: O acesso L2 é mais lento (~30ms versus ~10ms)

Políticas de remoção de cache

As políticas de expurgação controlam quando os tokens armazenados em cache são removidos. O código a seguir define a expiração absoluta e 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);
});

Você também pode configurar a evicção por meio de appsettings.json:

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

Recomendações:

  • Definir expiração maior que o tempo de vida do token (os tokens normalmente expiram em 1 hora)
  • Padrão: expiração deslizante de 90 minutos
  • Balancear entre o uso de memória e a experiência do usuário
  • Considere: 72 horas absolutas + 2 horas deslizantes para uma boa experiência do usuário

→ Saiba mais sobre estratégias de remoção de cache


Criptografia em repouso

Para proteger dados confidenciais de token em caches distribuídos, habilite a criptografia por meio da Proteção de Dados ASP.NET Core.

Única máquina

Em um único computador, habilite a criptografia com o provedor interno de Proteção de Dados:

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

Sistemas distribuídos (vários servidores)

Importante

Os sistemas distribuídos não compartilham chaves de criptografia por padrão. Você deve configurar o compartilhamento de chaves:

Azure Key Vault (recomendado):

O código a seguir persiste as chaves para Armazenamento de Blobs do Azure e as protege com 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 certificado:

O código a seguir persiste as chaves de um compartilhamento de arquivos e as protege 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 criptografia e proteção de dados


Considerações sobre o desempenho do cache

Use as estimativas a seguir para planejar a capacidade de cache para seu aplicativo.

Estimativas de tamanho do token

Tipo de token Tamanho Típico Por Observações
Tokens de aplicativo aproximadamente 2 KB Locatário × Recurso Removido automaticamente
Tokens de usuário ~7 KB Usuário × Locatário × Recurso Remoção manual necessária
Tokens de atualização Variable Usuário Vida longa

Planejamento de memória

Para 500 usuários simultâneos que chamam 3 APIs:

  • Tokens de usuário: 500 × 3 × 7 KB = 10,5 MB
  • Com sobrecarga: ~15 a 20 MB

Para 10.000 usuários simultâneos:

  • Tokens de usuário: 10.000 × 3 × 7 KB = 210 MB
  • Com sobrecarga: ~300-350 MB

Recomendação: Defina o limite de tamanho do cache L1 com base nos usuários simultâneos esperados.

Práticas recomendadas

Siga estas diretrizes para garantir o cache de token confiável e eficiente.

Usar cache distribuído em produção – Essencial para implantações de vários servidores

Definir limites de tamanho de cache apropriados – impedir o crescimento ilimitado da memória

Configurar políticas de despejo – Balancear o uso de memória e UX

Habilitar a criptografia para dados confidenciais – Proteger tokens em repouso

Monitorar a integridade do cache – controlar taxas de ocorrência, falhas e desempenho

Lidar com falhas de cache L2 normalmente – o cache L1 garante resiliência

Testar o comportamento do cache – verificar os cenários de reinicialização e failover

Não usar cache de memória distribuída em produção – não persistente ou distribuído

Não usar cache de sessão – tem limitações significativas

Defina a expiração não menor que o tempo de vida do token – pois isso forçará a reautenticação desnecessária

Não se esqueça do compartilhamento de chaves de criptografia – os sistemas distribuídos precisam de chaves compartilhadas