이 문서는 Microsoft.Identity.Web의 토큰 캐시 문제를 진단하고 해결하는 데 도움이 됩니다. 토큰 캐시 문제로 인해 인증 실패, 성능 저하 또는 예기치 않은 로그인 프롬프트가 발생할 수 있습니다. Microsoft.Identity.Web에서 토큰 캐싱의 작동 방식에 대한 개요는 토큰 캐시 개요를 참조하세요.
사전 요구 사항
문제를 해결하기 전에 다음을 확인합니다.
- Microsoft.Identity.Web의 지원되는 버전을 사용하고 있습니다.
- 애플리케이션은
Program.cs또는Startup.cs에 토큰 캐싱이 구성되어 있습니다. - 애플리케이션 로그 및 해당되는 경우 분산 캐시 인프라에 액세스할 수 있습니다.
토큰 캐시 로깅 및 진단 활성화
자세한 로깅을 첫 번째 진단 단계로 사용하도록 설정합니다. Microsoft. Identity.Web은 ASP.NET Core 로깅 인프라를 사용하고 MSAL(Microsoft 인증 라이브러리)을 통해 이벤트를 내보냅니다.
MSAL 로깅 활성화
ID 라이브러리의 로그 수준을 Debug으로 설정하십시오appsettings.json.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Identity.Web": "Debug",
"Microsoft.IdentityModel": "Debug"
}
}
}
MSAL 캐시 이벤트 구독
MSAL 토큰 캐시 알림 이벤트를 구독하여 캐시 적중, 미스 및 직렬화 작업을 추적합니다.
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDistributedTokenCaches();
services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
options.OnL2CacheFailure = (ex) =>
{
logger.LogWarning(ex, "L2 cache failure encountered.");
// Return true to allow the operation to continue despite the cache failure.
// Return false to propagate the exception.
return true;
};
});
캐시 메트릭 모니터링
프로덕션 모니터링의 경우 다음 주요 메트릭을 추적합니다.
- 캐시 적중률 - 적중률이 낮을 경우 토큰이 캐시에서 검색되지 않음을 나타냅니다.
- L2 캐시 대기 시간 - 높은 대기 시간은 분산 캐시 연결 또는 성능 문제를 시사합니다.
- 캐시 직렬화 오류 — 읽기 또는 쓰기 중 발생하는 오류는 손상 또는 버전 불일치를 나타냅니다.
- 메모리 사용 - 지속적인 증가는 누락된 제거 정책을 나타낼 수 있습니다.
L2(분산 캐시) 연결 실패
증상
애플리케이션 로그는 연결 시간 제한 오류 또는 일시적인 인증 실패를 표시합니다. 사용자에게 로그인 지연이 발생하며 다음과 같은 예외가 표시됩니다.
Microsoft.Extensions.Caching.StackExchangeRedis.RedisCache:
StackExchange.Redis.RedisConnectionException:
No connection is active/available to service this operation.
또는 SQL Server 분산 캐시의 경우:
Microsoft.Data.SqlClient.SqlException:
A network-related or instance-specific error occurred while
establishing a connection to SQL Server.
원인
분산 캐시 백업 저장소(Redis 또는 SQL Server)에 연결할 수 없습니다. 일반적인 원인은 다음과 같습니다.
- 잘못된 연결 문자열 또는 만료된 액세스 자격 증명입니다.
- 앱 호스트에서 연결을 차단하는 네트워크 방화벽 규칙입니다.
- 캐시 서비스가 중단되거나 유지 관리가 진행 중입니다.
- 클라이언트와 캐시 서버 간의 SSL/TLS 구성이 일치하지 않습니다.
진단 단계
다음 단계에 따라 연결 실패를 식별합니다.
- 연결을 확인합니다. 애플리케이션 호스트에서
Test-NetConnection(PowerShell) 또는redis-cli사용하여 Redis 또는 SQL Server 대한 연결을 테스트합니다. - 커넥션 스트링을 확인하십시오. 연결 문자열 캐시 서버의 호스트 이름, 포트 및 자격 증명과 일치하는지 확인합니다.
- 방화벽 규칙을 검토합니다. Azure 앱 서비스 또는 가상 네트워크가 캐시 리소스에 연결할 수 있는지 확인합니다.
- 서비스 상태를 확인하십시오. Azure 포털에서 Azure Cache for Redis 또는 SQL Database 인스턴스의 상태 및 메트릭을 검토합니다.
해결 방법
1단계: 연결 문자열 수정
appsettings.json의 연결 문자열을 확인하십시오.
{
"ConnectionStrings": {
"Redis": "your-redis-instance.redis.cache.windows.net:6380,password=your-access-key,ssl=True,abortConnect=False"
}
}
중요합니다
abortConnect=False를 Redis 연결 문자열에 설정하십시오. 이 설정을 사용하면 애플리케이션이 일시적 연결 실패 후 즉시 throw하지 않고 자동으로 다시 연결할 수 있습니다.
2단계: 재시도 및 복원력 구성
OnL2CacheFailure 콜백을 구성하여 분산 캐시가 일시적으로 사용할 수 없을 때 애플리케이션이 우아하게 저하되도록 합니다.
services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
options.OnL2CacheFailure = (ex) =>
{
// Log the failure for monitoring and alerting.
logger.LogWarning(ex, "Distributed token cache is unavailable. " +
"Falling back to in-memory cache.");
return true; // Continue without the L2 cache.
};
// Set a timeout to avoid blocking the request pipeline.
options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12);
});
3단계: 방화벽 규칙 열기
애플리케이션이 Azure App Service 실행되고 캐시가 가상 네트워크에 있는 경우 App Service 아웃바운드 IP 주소를 캐시 방화벽 허용 목록에 추가합니다.
캐시 역직렬화 오류
증상
Microsoft.Identity.Web 또는 MSAL.NET을 업그레이드한 후, 애플리케이션이 분산 캐시에서 데이터를 읽을 때, 역직렬화 예외를 발생시킵니다. 사용자는 다시 로그인해야 하며 다음과 같은 예외가 표시됩니다.
System.Text.Json.JsonException:
The JSON value could not be converted to the expected type.
또는:
Microsoft.Identity.Client.MsalClientException:
Error code: json_parse_failed
원인
라이브러리 버전 간 토큰 캐시 직렬화 형식이 변경되었습니다. 이전 버전에서 캐시한 토큰은 새 버전에서 역직렬화할 수 없습니다. 이 문제는 MSAL.NET 또는 Microsoft.Identity.Web의 주요 버전 업그레이드 중에 가장 자주 발생합니다.
해결 방법
옵션 A: 캐시 지우기
가장 간단한 해결 방법은 분산 캐시의 모든 항목을 지우는 것입니다. 사용자는 한 번 다시 인증하고 이후 토큰은 새 형식으로 작성됩니다.
Redis 캐시를 플러시합니다.
redis-cli FLUSHDB
또는 SQL Server 분산 캐시 테이블을 지웁다.
DELETE FROM [dbo].[TokenCache];
메모
캐시를 지우면 모든 활성 사용자가 다시 인증됩니다. 애플리케이션이 대규모 사용자 기반을 제공하는 경우 유지 관리 기간 동안 이 작업을 계획합니다.
옵션 B: 역직렬화 오류를 정상적으로 처리
역직렬화 오류를 심각한 오류가 아닌 캐시 누락으로 처리하도록 캐시 어댑터를 구성합니다.
services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
options.OnL2CacheFailure = (ex) =>
{
if (ex is JsonException or MsalClientException)
{
logger.LogWarning(ex, "Cache deserialization failed. " +
"Treating as cache miss.");
return true;
}
return false; // Propagate unexpected errors.
};
});
이 방법을 사용하면 영향을 받는 캐시 항목이 사용자가 다시 인증할 때 자동으로 대체되며 수동 캐시 플러시가 필요하지 않습니다.
서버 간 암호화 키 불일치
증상
분산 캐시가 작동하는 경우에도 다중 인스턴스 배포에서 역직렬화 오류가 발생합니다. 한 서버 인스턴스에서 캐시한 토큰은 다른 서버 인스턴스에서 읽을 수 없습니다. 로그에서 json_parse_failed 또는 IDW10802 오류를 확인할 수 있습니다.
원인
캐시 암호화를 사용하는 경우(options.Encrypt = true) Microsoft. Identity.Web은 ASP.NET Core Data Protection을 사용하여 캐시 항목을 암호화합니다. 기본적으로 각 서버 인스턴스는 자체 데이터 보호 키를 생성하므로 한 인스턴스는 다른 인스턴스에서 작성한 항목의 암호를 해독할 수 없습니다.
해결 방법
모든 서버 인스턴스에서 암호화 키를 공유하도록 ASP.NET Core Data Protection을 구성합니다.
옵션 A: Azure Blob Storage + Azure Key Vault(Azure 배포에 권장)
using Microsoft.AspNetCore.DataProtection;
using Azure.Identity;
builder.Services.AddDataProtection()
.PersistKeysToAzureBlobStorage(
new Uri("https://yourstorageaccount.blob.core.windows.net/dataprotection/keys.xml"),
new DefaultAzureCredential())
.ProtectKeysWithAzureKeyVault(
new Uri("https://yourkeyvault.vault.azure.net/keys/dataprotection-key"),
new DefaultAzureCredential());
builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
options.Encrypt = true;
});
이 구성은 데이터 보호 키 링을 Azure Blob Storage 저장하고 Azure Key Vault 사용하여 미사용 키를 보호합니다. 동일한 Blob 및 키에 액세스하는 모든 애플리케이션 인스턴스는 서로의 캐시 항목을 암호화하고 암호를 해독할 수 있습니다.
옵션 B: 인증서 보호를 사용하는 공유 파일 시스템
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\keys"))
.ProtectKeysWithCertificate(certificate);
팁 (조언)
데이터 보호 인증서를 회전할 때 현재 인증서와 이전 인증서를 모두 포함하는 데 사용합니다 UnprotectKeysWithAnyCertificate . 이렇게 하면 회전 기간 동안 이전 인증서로 보호된 키를 해독할 수 있습니다.
인메모리 캐시를 통한 메모리 증가
증상
애플리케이션 메모리 사용량은 시간이 지남에 따라 꾸준히 증가합니다. 애플리케이션이 고정 메모리 제한을 가진 컨테이너 또는 App Service 계획에서 실행되면 결국 재시작되거나 예외를 발생시킵니다 OutOfMemoryException. 모니터링은 가비지 수집에 의한 매립 없이 증가하는 관리되는 힙을 보여줍니다.
원인
크기 제한 없이 AddInMemoryTokenCaches()를 사용하면 무제한으로 캐시가 증가합니다. 이 상황은 각 사용자의 토큰 항목이 메모리를 무기한 소비하기 때문에 많은 사용자에게 서비스를 제공하는 애플리케이션에서 특히 문제가 됩니다.
기본적으로 MemoryCache 최대 크기를 적용하지 않으며 만료 정책이 설정되지 않는 한 항목을 제거하지 않습니다.
해결 방법
옵션 A: 크기 제한 및 슬라이딩 만료 설정
만료 정책을 사용하여 메모리 내 캐시를 구성합니다.
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
services.Configure<MsalMemoryTokenCacheOptions>(options =>
{
options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12);
options.SlidingExpiration = TimeSpan.FromHours(2);
});
이러한 설정을 사용하면 액세스에 관계없이 12시간 후에 항목이 만료되고 2시간 동안 유휴 상태인 항목이 더 일찍 제거됩니다.
옵션 B: 분산 캐시로 전환
동시 사용자가 많은 애플리케이션의 경우 메모리 내 캐시의 크기가 조정되지 않습니다. Redis와 같은 분산 캐시로 전환합니다.
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = Configuration.GetConnectionString("Redis");
});
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDistributedTokenCaches();
분산 캐시는 애플리케이션 프로세스에서 메모리를 오프로드하고, 다시 시작할 때 토큰을 유지하며, 다중 인스턴스 배포를 지원합니다.
옵션 C: L1/L2 하이브리드 아키텍처 사용
Microsoft. Identity.Web은 빠른 메모리 내 L1 캐시와 영구 분산 L2 캐시를 결합하는 하이브리드 접근 방식을 지원합니다. L1/L2 하이브리드 캐시를 구성합니다.
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDistributedTokenCaches();
services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
options.L1CacheOptions = new MsalMemoryTokenCacheOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5),
SlidingExpiration = TimeSpan.FromMinutes(2)
};
});
L1/L2 캐싱을 사용하면 자주 액세스하는 토큰은 메모리 내(L1)에서 밀리초 미만의 대기 시간으로 제공됩니다. L2 캐시는 지속성과 인스턴스 간 일관성을 제공합니다. L1 캐시는 짧은 만료를 사용하여 메모리 증가를 제한합니다.
반복되는 MFA 또는 동의 프롬프트
증상
사용자는 최근에 이러한 단계를 완료했음에도 불구하고 MFA(다단계 인증) 또는 동의를 묻는 메시지가 반복적으로 표시됩니다. 애플리케이션이 캐시에서 기존 토큰을 찾을 수 없습니다.
원인
이 문제는 토큰 캐시 조회가 현재 사용자 계정에 캐시된 항목과 일치하지 않을 때 발생합니다. 일반적인 원인은 다음과 같습니다.
- 캐시 키는 토큰이 저장될 때 사용된 키와 다릅니다. 이 상황은
HomeAccountId또는 테넌트 컨텍스트가 변경되는 경우에 발생할 수 있습니다. - 애플리케이션은 메모리 내 캐싱을 사용하여 부하 분산 장치 뒤에서 여러 인스턴스를 실행하고 사용자의 토큰이 없는 인스턴스로 경로를 요청합니다.
- 요청된 클레임 또는 범위가 변경되어 캐시된 토큰이 새 요구 사항을 충족하지 않습니다.
- 세션 선호도는 사용하도록 설정되지 않으므로 사용자는 캐시된 토큰이 없는 다른 인스턴스로 라우팅됩니다.
진단 단계
다음 단계에 따라 캐시에서 토큰을 찾을 수 없는 이유를 식별합니다.
- 캐시 유형을 확인합니다. 다중 인스턴스 배포에서 사용하는
AddInMemoryTokenCaches()경우 한 인스턴스에 캐시된 토큰은 다른 인스턴스에서 사용할 수 없습니다. 분산 캐시로 전환합니다. - 계정 식별자를 확인합니다. 디버그 수준 로깅을 사용하도록 설정하고
HomeAccountId를 검색하십시오. 식별자가 요청 간에 일관적인지 확인합니다. - 범위를 검사합니다. 요청
GetAccessTokenForUserAsync된 범위가 원래 동의한 범위와 일치하는지 확인합니다. 범위가 일치하지 않는 경우 MSAL은 새 토큰을 요청합니다. - 조건부 액세스 정책을 검토합니다. 특정 리소스에 대한 단계별 인증이 필요한 Microsoft Entra ID 조건부 액세스 정책은 캐싱과 관련이 없는 추가 프롬프트를 발생합니다.
해결 방법
1단계: 분산 캐싱으로 전환
애플리케이션이 여러 인스턴스를 실행하는 경우 분산 캐시를 사용하여 인스턴스 간에 토큰을 공유합니다.
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = Configuration.GetConnectionString("Redis");
});
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDistributedTokenCaches();
2단계: 일관된 범위 확인
토큰을 획득할 때 요청하는 범위가 인증 중에 구성된 범위와 일치하는지 확인합니다.
// In authentication setup — initial scopes.
.EnableTokenAcquisitionToCallDownstreamApi(new[] { "User.Read", "Mail.Read" })
// When acquiring a token — use the same scopes.
var token = await tokenAcquisition.GetAccessTokenForUserAsync(
new[] { "User.Read", "Mail.Read" });
3단계: 세션 선호도 사용(임시 해결 방법)
분산 캐시로 바로 전환할 수 없는 경우 부하 분산 장치에서 세션 선호도(고정 세션)를 사용하도록 설정합니다. 세션 선호도는 사용자의 요청을 동일한 인스턴스로 라우팅합니다. 이 방법은 확장성 제한이 있는 임시 해결 방법입니다.
캐시 성능 문제
증상
토큰 검색이 느리고 다운스트림 API 호출의 대기 시간이 증가했습니다. 모니터링은 토큰 획득 요청에 대한 높은 평균 응답 시간을 보여 줍니다. 대기 시간은 ID 공급자가 아닙니다. 토큰은 캐시에서 제공됩니다.
원인
캐시 성능 문제는 일반적으로 다음에서 발생합니다.
- 높은 L2 캐시 대기 시간. 분산 캐시는 부하가 많거나, 애플리케이션에서 지리적으로 멀거나, 크기가 작은 서비스 계층을 사용하고 있습니다.
- 대형 토큰 캐시 엔트리. 사용자당 많은 리소스에 대한 토큰을 캐시하는 애플리케이션은 읽기 및 쓰기 속도가 느린 대규모 직렬화된 캐시 항목을 생성할 수 있습니다.
- L1 캐시가 없습니다. 모든 토큰 획득은 자주 사용되는 토큰의 경우에도 네트워크를 통해 분산 캐시로 이동합니다.
해결 방법
1단계: L1 메모리 내 캐시 사용
L1 캐시는 자주 액세스하는 토큰을 프로세스 메모리에 저장하므로 L2로의 네트워크 왕복을 방지합니다.
services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
options.L1CacheOptions = new MsalMemoryTokenCacheOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5),
SlidingExpiration = TimeSpan.FromMinutes(2)
};
});
이 구성을 사용하면 L1에서 제공되는 토큰에는 밀리초 미만의 대기 시간이 있습니다. L1에 없는 토큰은 L2 분산 캐시로 대체됩니다.
2단계: 분산 캐시 계층 최적화
L2 캐시 대기 시간이 높은 경우 다음 작업을 고려합니다.
- Redis 인스턴스를 강화합니다. 처리량을 높이고 대기 시간을 줄이려면 더 높은 계층(예: Azure Cache for Redis 기본에서 표준으로 또는 프리미엄)으로 이동합니다.
- 지리적 복제를 활성화하다. 애플리케이션이 여러 지역의 사용자에게 서비스를 제공하는 경우 캐시가 각 지역의 컴퓨팅에 근접하도록 Azure Cache for Redis 지역 복제를 사용합니다.
- 네트워크 구성을 검토합니다. Private Link 또는 VNet 통합을 사용하여 애플리케이션과 캐시 간의 네트워크 홉을 줄입니다.
3단계: 직렬화된 토큰 크기 줄이기
토큰 캐시 항목이 큰 경우 애플리케이션이 필요한 것보다 많은 리소스에 대한 토큰을 요청하는지 검토합니다. 각 고유한 리소스 및 범위 조합은 캐시 항목 크기에 추가됩니다. 가능한 경우 API 호출을 통합하여 사용자당 캐시된 고유 액세스 토큰의 수를 줄입니다.
Redis 캐시 제거
증상
토큰 만료에 따라 패턴 없이 다시 인증하라는 메시지가 사용자에게 간헐적으로 표시됩니다. Redis 모니터링은 evicted_keys 증가하고 used_memory가 maxmemory 한도에 접근하는 것을 관찰합니다.
원인
Redis가 제한에 maxmemory 도달하면 구성된 maxmemory-policy키를 기반으로 키를 제거합니다. 기본 정책(volatile-lru)은 만료가 있는 가장 최근에 사용한 키를 제거합니다. Redis 인스턴스가 다른 애플리케이션 데이터와 공유되는 경우 토큰 캐시 항목은 공간을 놓고 경쟁하며 조기에 제거할 수 있습니다.
해결 방법
1단계: 제거 정책 확인
현재 제거 정책을 확인합니다.
redis-cli CONFIG GET maxmemory-policy
토큰 캐시의 volatile-lru 경우 토큰 캐시 항목에 만료가 있기 때문에 (기본값) 적합합니다. 그러나 만료가 없는 다른 데이터가 메모리를 사용하는 경우 토큰 항목이 먼저 제거됩니다.
2단계: 전용 Redis 인스턴스 사용
전용 Redis 인스턴스를 사용하여 다른 애플리케이션 데이터에서 토큰 캐시를 격리합니다.
{
"ConnectionStrings": {
"RedisTokenCache": "token-cache-redis.redis.cache.windows.net:6380,password=...,ssl=True,abortConnect=False",
"RedisAppData": "app-data-redis.redis.cache.windows.net:6380,password=...,ssl=True,abortConnect=False"
}
}
// Register the token cache Redis instance specifically for distributed caching.
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = Configuration.GetConnectionString("RedisTokenCache");
});
3단계: Redis 메모리 제한 늘리기
전용 인스턴스를 사용할 수 없는 경우 설정을 늘입니다 maxmemory . Azure Cache for Redis 더 높은 계층으로 확장하거나 캐시 크기를 늘입니다.
4단계: 적절한 캐시 항목 만료 설정
메모리가 다 떨어지기 전에 부실 항목이 제거되도록 적절한 만료를 설정합니다.
services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
options.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12);
options.SlidingExpiration = TimeSpan.FromHours(2);
});
SQL 분산 캐시 테이블 증가
증상
SQL 분산 캐시 테이블은 디스크 공간을 소비하면서 지속적으로 증가합니다. 캐시 테이블에 대한 데이터베이스 쿼리는 시간이 지남에 따라 느려지고 테이블 크기 또는 스토리지 제한에 대한 경고가 표시될 수 있습니다.
원인
SQL Server 분산 캐시(Microsoft.Extensions.Caching.SqlServer)는 만료된 항목을 자동으로 제거하지 않습니다. 만료된 항목은 명시적으로 제거될 때까지 유지되며, 이로 인해 무제한 테이블 증가, 쿼리 성능 저하 및 스토리지 사용이 발생합니다.
해결 방법
1단계: 되풀이 정리 작업 설정
만료된 항목을 주기적으로 제거하는 SQL Server 에이전트 작업 또는 예약된 작업을 만듭니다.
-- Delete expired entries from the SQL distributed cache table.
-- Schedule this query to run every 30 minutes.
DELETE FROM [dbo].[TokenCache]
WHERE ExpiresAtTime < GETUTCDATE();
팁 (조언)
SQL Server 에이전트를 사용할 수 없는 Azure SQL Database에서는 Azure Automation, 타이머 트리거가 있는 Azure Functions, 또는 Elastic Job을 사용하여 정리를 예약합니다.
2단계: 효율적인 정리를 위한 인덱스 추가
캐시 테이블에 만료 열에 인덱스가 아직 없는 경우 삭제 작업 속도를 높이기 위해 인덱스 하나를 추가합니다.
CREATE NONCLUSTERED INDEX IX_TokenCache_ExpiresAtTime
ON [dbo].[TokenCache] (ExpiresAtTime);
3단계: 테이블 크기 모니터링
시간에 따른 행 수 및 테이블 크기를 추적하는 모니터링을 추가합니다.
SELECT
COUNT(*) AS TotalEntries,
COUNT(CASE WHEN ExpiresAtTime < GETUTCDATE() THEN 1 END) AS ExpiredEntries,
COUNT(CASE WHEN ExpiresAtTime >= GETUTCDATE() THEN 1 END) AS ActiveEntries
FROM [dbo].[TokenCache];
4단계: Redis로 전환하는 것이 좋습니다.
SQL 캐시 정리 관리가 부담스러운 경우 기본 제공 TTL 메커니즘을 통해 만료를 자동으로 처리하는 Redis로 전환합니다.
// Replace SQL distributed cache with Redis.
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = Configuration.GetConnectionString("Redis");
});
일반 문제 해결 팁
문제가 이 문서의 특정 시나리오와 일치하지 않는 경우 이러한 팁을 사용합니다.
캐시가 사용되고 있는지 확인
임시 로깅을 추가하여 토큰이 캐시에서 읽고 캐시에 기록되는지 확인합니다.
services.Configure<MsalDistributedTokenCacheAdapterOptions>(options =>
{
options.Encrypt = false; // Disable encryption temporarily for debugging only.
options.OnL2CacheFailure = (ex) =>
{
logger.LogError(ex, "L2 cache operation failed.");
return true;
};
});
여러 캐시 등록 확인
시작 코드에서 AddInMemoryTokenCaches() 또는 AddDistributedTokenCaches()에 대한 호출이 여러 개 있는 경우, 마지막 등록이 우선합니다. 하나의 캐시 유형만 등록되어 있는지 확인합니다.
토큰 수명 검토
액세스 토큰의 수명은 한정됩니다(일반적으로 60~90분). 이 기간 이후에 사용자가 다시 인증을 보고하면 캐시 문제가 아닌 동작이 예상됩니다. 새로 고침 토큰은 새 액세스 토큰을 자동으로 가져오고 캐시에 저장됩니다. 새로 고침 토큰이 없거나 만료된 경우 사용자는 다시 인증해야 합니다.
클린 캐시로 테스트
문제를 진단할 때 캐시를 지우면 손상된 항목이나 부실 항목을 제외합니다.
- 메모리 내 캐시: 애플리케이션을 다시 시작합니다.
-
Redis: 캐시 데이터베이스에서 실행
FLUSHDB합니다. - SQL Server: 캐시 테이블에서 모든 행을 삭제합니다.
애플리케이션을 다시 시작한 후 토큰 캐시가 비어 있음
증상
사용자는 애플리케이션을 다시 시작하거나 다시 배포할 때마다 다시 인증해야 합니다. 분산 캐시가 비어 있거나 토큰이 유지되지 않습니다.
원인
이 문제는 일반적으로 프로덕션 환경에서 메모리 내 캐시(AddInMemoryTokenCaches()) 또는 비영구 분산 메모리 캐시(AddDistributedMemoryCache())를 사용할 때 발생합니다. 두 옵션 모두 애플리케이션을 다시 시작할 때 토큰을 유지하지 않습니다.
AddDistributedMemoryCache() 는 메모리에 IDistributedCache 데이터를 저장하는 구현을 등록합니다. "분산" 이름에도 불구하고 데이터를 외부적으로 유지하지 않으며 개발 및 테스트용으로만 사용됩니다.
해결 방법
영구 분산 캐시로 전환:
// Register a persistent cache (Redis example).
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = builder.Configuration.GetConnectionString("Redis");
options.InstanceName = "MyApp_";
});
// Use distributed token caches instead of in-memory.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDistributedTokenCaches();
경고
영구 분산 캐시와 혼동 AddDistributedMemoryCache() 하지 마세요. 프로덕션 워크로드에 AddStackExchangeRedisCache()(Redis), AddDistributedSqlServerCache()(SQL Server) 또는 다른 영구 IDistributedCache 구현을 사용합니다.