Microsoft.Identity.Web로 보호되는 ASP.NET Core 웹 API를 Azure API 게이트웨이 및 역방향 프록시 뒤에 배포합니다. 여기에는 Azure API Management (APIM), Azure Front Door, Azure Application Gateway가 포함됩니다.
게이트웨이 요구 사항 이해
게이트웨이 뒤에 보호된 API를 배포하는 경우 다음과 같은 몇 가지 문제를 처리해야 합니다.
- 전달된 헤더 - 원래 요청 컨텍스트 유지(체계, 호스트, IP)
- 토큰 유효성 검사 - 대상 그룹 클레임이 게이트웨이 URL과 일치하는지 확인
- CORS 구성 - 원본 간 요청을 올바르게 처리
- 상태 엔드포인트 - 인증되지 않은 상태 검사 제공
- 경로 기반 라우팅 - 게이트웨이 수준 경로 접두사 지원
- SSL/TLS 종료 - 게이트웨이가 SSL을 종료할 때 HTTPS를 올바르게 처리
일반적인 게이트웨이 시나리오 검토
요구 사항에 따라 게이트웨이를 선택합니다. 다음 섹션에서는 보호된 API에 대한 가장 일반적인 Azure 게이트웨이 서비스에 대해 설명합니다.
AZURE API MANAGEMENT(APIM)
사용 사례: 정책, 속도 제한, 변환을 사용하는 엔터프라이즈 API 게이트웨이
아키텍처:
Client → Microsoft Entra ID → Token
Client → APIM (apim.azure-api.net) → Backend API (app.azurewebsites.net)
주요 고려 사항:
- APIM 정책은 백 엔드로 전달하기 전에 JWT 토큰의 유효성을 검사할 수 있습니다.
- 백 엔드 API는 여전히 토큰의 유효성을 검사합니다.
- 대상 그룹 클레임은 APIM URL 또는 백 엔드 URL과 일치해야 합니다(그에 따라 구성).
Azure Front Door
사용 사례: 전역 부하 분산, CDN, DDoS 보호
아키텍처:
Client → Microsoft Entra ID → Token
Client → Front Door (azurefd.net) → Backend API (regional endpoints)
주요 고려 사항:
- Front Door는 헤더를 사용하여
X-Forwarded-*요청을 전달합니다. - Front Door에서 SSL/TLS 종료
- 토큰 대상 그룹 유효성 검사에 구성 필요
Azure Application Gateway
사용 사례: 지역 부하 분산, WAF, 경로 기반 라우팅
아키텍처:
Client → Microsoft Entra ID → Token
Client → Application Gateway → Backend API (multiple instances)
주요 고려 사항:
- WAF(Web Application Firewall) 통합
- 경로 기반 라우팅 규칙
- 백 엔드 상태 프로브에는 인증되지 않은 엔드포인트가 필요합니다.
일반적인 패턴 구성
이러한 구성 패턴을 적용하여 보호된 API가 게이트웨이 뒤에서 올바르게 작동하는지 확인합니다.
1. 전달된 헤더 미들웨어
게이트웨이 뒤에 있을 때는 반드시 전달 헤더 미들웨어를 구성하세요. 다음 코드는 미들웨어를 등록하고 인증 전에 실행되도록 설정합니다.
using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args);
// Configure forwarded headers BEFORE authentication
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto |
ForwardedHeaders.XForwardedHost;
// Clear known networks/proxies to accept forwarded headers from any source
// (Azure infrastructure will be the proxy)
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
// Limit to specific headers if needed
options.ForwardedForHeaderName = "X-Forwarded-For";
options.ForwardedProtoHeaderName = "X-Forwarded-Proto";
options.ForwardedHostHeaderName = "X-Forwarded-Host";
});
// Add authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
var app = builder.Build();
// USE forwarded headers BEFORE authentication middleware
app.UseForwardedHeaders();
app.UseAuthentication();
app.UseAuthorization();
app.Run();
전달된 헤더 미들웨어는 다음과 같은 이유로 중요합니다.
- 로깅을 위해 원래 클라이언트 IP 주소를 유지합니다.
-
HttpContext.Request.Scheme원래 HTTPS 스키마를 반영하는지 확인합니다. - 리디렉션 URL 및 토큰 유효성 검사에 대한 올바른
Host헤더를 제공합니다.
2. 토큰 대상 구성
옵션 A: 게이트웨이 및 백 엔드 URL 모두 허용
구성에 유효한 여러 대상 그룹을 추가합니다 appsettings.json .
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-client-id",
"Audience": "api://your-client-id",
"TokenValidationParameters": {
"ValidAudiences": [
"api://your-client-id",
"https://your-backend.azurewebsites.net",
"https://your-apim.azure-api.net"
]
}
}
}
또는 다음에서 프로그래밍 방식으로 여러 대상 그룹을 구성합니다.Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
// Customize token validation to accept multiple audiences
builder.Services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
var existingValidation = options.TokenValidationParameters.AudienceValidator;
options.TokenValidationParameters.AudienceValidator = (audiences, token, parameters) =>
{
var validAudiences = new[]
{
"api://your-client-id",
"https://your-backend.azurewebsites.net",
"https://your-apim.azure-api.net",
builder.Configuration["AzureAd:ClientId"] // Also accept ClientId
};
return audiences.Any(a => validAudiences.Contains(a, StringComparer.OrdinalIgnoreCase));
};
});
옵션 B: APIM 정책에서 대상 그룹 다시 쓰기
백 엔드로 전달하기 전에 대상 그룹 클레임의 유효성을 검사하도록 APIM을 구성합니다.
<policies>
<inbound>
<validate-jwt header-name="Authorization" failed-validation-httpcode="401">
<openid-config url="https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration" />
<audiences>
<audience>api://your-client-id</audience>
</audiences>
</validate-jwt>
<!-- Optionally modify token claims for backend -->
<set-header name="X-Gateway-Validated" exists-action="override">
<value>true</value>
</set-header>
</inbound>
</policies>
3. 헬스 엔드포인트 구성
게이트웨이는 프로브를 위해 인증되지 않은 상태 검사 엔드포인트가 필요합니다. 인증 미들웨어 앞에 상태 엔드포인트를 매핑하여 토큰 유효성 검사를 무시합니다.
var app = builder.Build();
// Health endpoint BEFORE authentication middleware
app.MapGet("/health", () => Results.Ok(new { status = "healthy" }))
.AllowAnonymous();
app.UseForwardedHeaders();
app.UseAuthentication();
app.UseAuthorization();
// Protected endpoints require authentication
app.MapControllers();
app.Run();
또는 더 풍부한 상태 보고를 위해 기본 제공 ASP.NET Core 상태 검사 프레임워크를 사용합니다.
using Microsoft.Extensions.Diagnostics.HealthChecks;
builder.Services.AddHealthChecks()
.AddCheck("api", () => HealthCheckResult.Healthy());
var app = builder.Build();
app.MapHealthChecks("/health").AllowAnonymous();
app.MapHealthChecks("/ready").AllowAnonymous();
app.UseForwardedHeaders();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
4. 게이트웨이 뒤의 CORS 구성
프런트 엔드 애플리케이션에서 Azure Front Door 또는 APIM을 사용하는 경우 게이트웨이 원본의 요청을 허용하도록 CORS를 구성합니다.
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowGateway", policy =>
{
policy.WithOrigins(
"https://your-apim.azure-api.net",
"https://your-frontend.azurefd.net",
"https://your-app.azurewebsites.net"
)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials(); // If using cookies
});
});
var app = builder.Build();
app.UseForwardedHeaders();
app.UseCors("AllowGateway");
app.UseAuthentication();
app.UseAuthorization();
app.Run();
Important
CORS는 전달된 헤더 후 및 인증 전에 구성해야 합니다.
Azure API Management과 통합하십시오
이 섹션에서는 Azure API Management 뒤에 보호된 API를 배포하기 위한 전체 구성을 제공합니다.
백 엔드 API 구성
Program.cs에서 전달된 헤더와 Microsoft Entra ID 인증을 설정합니다.
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
// Forwarded headers for APIM
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.All;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
// Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddControllers();
var app = builder.Build();
// Middleware order matters
app.UseForwardedHeaders();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
appsettings.json에 Microsoft Entra 구성을 추가합니다.
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-backend-api-client-id",
"Audience": "api://your-backend-api-client-id"
}
}
JWT 유효성 검사를 위한 APIM 인바운드 정책 추가
JWT 토큰의 유효성을 검사하고, 속도 제한을 적용하고, 백 엔드에 요청을 전달하는 인바운드 정책을 정의합니다.
<policies>
<inbound>
<base />
<!-- Validate JWT token -->
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized">
<openid-config url="https://login.microsoftonline.com/{your-tenant-id}/v2.0/.well-known/openid-configuration" />
<audiences>
<audience>api://your-backend-api-client-id</audience>
</audiences>
<issuers>
<issuer>https://login.microsoftonline.com/{your-tenant-id}/v2.0</issuer>
</issuers>
<required-claims>
<claim name="scp" match="any">
<value>access_as_user</value>
</claim>
</required-claims>
</validate-jwt>
<!-- Rate limiting -->
<rate-limit calls="100" renewal-period="60" />
<!-- Forward original host header -->
<set-header name="X-Forwarded-Host" exists-action="override">
<value>@(context.Request.OriginalUrl.Host)</value>
</set-header>
<!-- Forward to backend -->
<set-backend-service base-url="https://your-backend.azurewebsites.net" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
APIM API 설정 구성
다음 명명된 값 및 API 설정을 사용하여 APIM 구성을 완료합니다.
명명된 값(재사용 가능):
-
tenant-id: Microsoft Entra 테넌트 ID -
backend-api-client-id: 백 엔드 API의 클라이언트 ID -
backend-base-url:https://your-backend.azurewebsites.net
API 설정:
-
API URL 접미사:
/api(선택적 경로 접두사) - 웹 서비스 URL: 명명된 값을 사용하여 정책을 통해 설정
- 구독 필요: 예(다른 보안 계층 추가)
클라이언트 애플리케이션 구성
클라이언트 앱은 APIM이 아닌 백 엔드 API에 대한 토큰을 요청합니다. 다음 코드는 토큰을 획득하고 APIM 엔드포인트를 통해 API를 호출합니다.
// Client app requests token
var result = await app.AcquireTokenSilent(
scopes: new[] { "api://your-backend-api-client-id/access_as_user" },
account)
.ExecuteAsync();
// Call APIM URL with token
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", result.AccessToken);
// Add APIM subscription key
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "your-subscription-key");
var response = await client.GetAsync("https://your-apim.azure-api.net/api/weatherforecast");
Azure Front Door와 통합하세요
Azure Front Door 뒤에서 전역 배포를 위해 보호된 API를 구성합니다.
백 엔드 API 구성
Program.cs의 Azure Front Door에 대한 헤더 전달을 설정합니다.
using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args);
// Configure for Azure Front Door
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto |
ForwardedHeaders.XForwardedHost;
// Accept headers from any source (Azure Front Door)
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
// Front Door specific headers
options.ForwardedForHeaderName = "X-Forwarded-For";
options.ForwardedProtoHeaderName = "X-Forwarded-Proto";
});
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
var app = builder.Build();
app.UseForwardedHeaders();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Front Door 원본 구성
Azure 포털에서 다음 단계를 완료하여 Front Door 원본을 설정합니다.
- Front Door 프로필 만들기
- 백 엔드 API 인스턴스를 사용하여 원본 그룹 추가
- 엔드포인트에
/health상태 프로브 구성 - HTTPS 전달만 설정
- WAF 정책 사용(선택 사항)
헬스 프로브 설정:
-
경로:
/health - 프로토콜: HTTPS
- 메서드: GET
- 간격: 30초
여러 지역 처리
Front Door 뒤에 있는 여러 지역에 배포하는 경우 로깅 및 진단에 대한 지역 인식을 추가합니다.
// Add region awareness for logging/diagnostics
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
app.Use(async (context, next) =>
{
// Log the actual client IP and region
var clientIp = context.Connection.RemoteIpAddress?.ToString();
var forwardedFor = context.Request.Headers["X-Forwarded-For"].ToString();
var frontDoorId = context.Request.Headers["X-Azure-FDID"].ToString();
// Add to logger scope or response headers
context.Response.Headers.Add("X-Served-By-Region",
builder.Configuration["Region"] ?? "unknown");
await next();
});
Front Door를 사용하여 토큰 유효성 검사
클라이언트가 Front Door URL로 범위가 지정된 토큰을 요청하는 경우 유효한 대상 그룹 목록에 추가합니다.
builder.Services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters.ValidAudiences = new[]
{
"api://your-backend-api-client-id",
"https://your-frontend.azurefd.net", // Front Door URL
builder.Configuration["AzureAd:ClientId"]
};
});
Azure Application Gateway와 통합
WAF(Web Application Firewall) 지원을 사용하여 Azure Application Gateway 뒤에서 보호된 API를 구성합니다.
백 엔드 API 구성
다음에서 Application Gateway에 대해 전달된 헤더를 설정합니다.Program.cs
using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args);
// Application Gateway uses standard forwarded headers
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddHealthChecks();
var app = builder.Build();
// Health endpoint for Application Gateway probes
app.MapHealthChecks("/health").AllowAnonymous();
app.UseForwardedHeaders();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Application Gateway 설정 구성
Azure 포털에서 다음 백 엔드, 상태 프로브 및 WAF 설정을 설정합니다.
백 엔드 설정:
- 프로토콜: HTTPS(권장) 또는 HTTP
- 포트: 443 또는 80
- 백 엔드 경로 재정의: 아니요(필요 없는 경우)
-
사용자 정의 탐침: 예,
/health
헬스 프로브:
- 프로토콜: HTTPS 또는 HTTP
- 호스트: 기본값으로 유지하거나 지정
-
경로:
/health - 간격: 30초
- 비정상 임계값: 3
WAF 정책:
- OWASP 3.2 규칙 집합을 사용하여 WAF 사용
-
중요: 헤더의 JWT 토큰이
Authorization차단되지 않았는지 확인 -
RequestHeaderNames에 "Authorization"이 포함된 경우 WAF 제외를 만들어야 할 수 있습니다.
경로 기반 라우팅 설정
경로 기반 라우팅 규칙을 사용하는 경우 경로 접두사를 처리하도록 백 엔드 API를 구성합니다.
// Backend API should work regardless of path prefix
var app = builder.Build();
// Option 1: Use path base (if gateway adds prefix)
app.UsePathBase("/api/v1");
// Option 2: Configure routing explicitly
app.UseForwardedHeaders();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Application Gateway 규칙:
-
경로:
/api/v1/* - 백엔드 대상: 백엔드 풀
- 백 엔드 설정: 구성된 설정 사용
일반적인 문제 해결
이러한 솔루션을 사용하여 게이트웨이 뒤에 보호된 API를 배포할 때 가장 일반적인 문제를 해결합니다.
문제: 게이트웨이 경유 배포 후 401 권한이 없습니다
증상:
- API는 로컬에서 작동하지만 게이트웨이 뒤에 401을 반환합니다.
- jwt.ms 디코딩할 때 토큰이 유효한 것 같습니다.
가능한 원인:
대상 주장 불일치
# Check token audience # Decode token and verify 'aud' claim matches one of: # - api://your-client-id # - https://your-backend.azurewebsites.net # - https://your-gateway-url전달된 헤더 미들웨어 누락
// Ensure this is BEFORE authentication app.UseForwardedHeaders(); app.UseAuthentication();HTTPS 리디렉션 문제
// If gateway terminates SSL, may need to disable or configure carefully if (!app.Environment.IsDevelopment()) { app.UseHttpsRedirection(); }
Solution:
- 디버그 로깅을 사용하도록 설정하여 토큰 유효성 검사 세부 정보 보기
- 토큰 유효성 검사에 여러 유효한 대상 그룹 추가
- 게이트웨이에서
X-Forwarded-*헤더를 전달했는지 확인합니다.
문제: 상태 프로브 실패
증상:
- 게이트웨이는 백 엔드를 비정상으로 표시합니다.
- 건강 엔드포인트가 401을 반환합니다.
Solution:
인증 미들웨어 전에 상태 엔드포인트가 실행되는지 확인합니다.
// Ensure health endpoint is BEFORE authentication
app.MapHealthChecks("/health").AllowAnonymous();
// Alternative: Use custom middleware
app.Map("/health", healthApp =>
{
healthApp.Run(async context =>
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("healthy");
});
});
app.UseAuthentication(); // Health endpoint bypasses this
문제: Front Door 뒤의 CORS 오류
증상:
- 실행 전 OPTIONS 요청 실패
- 브라우저 콘솔에서 CORS 오류를 표시합니다.
Solution:
CORS 정책에 Front Door 및 프론트엔드 원본을 추가하십시오.
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.WithOrigins(
"https://your-frontend.azurefd.net",
"https://your-app.com"
)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
var app = builder.Build();
app.UseForwardedHeaders();
app.UseCors(); // Before authentication
app.UseAuthentication();
app.UseAuthorization();
문제: 로그의 "전달된 헤더" 경고
증상:
Microsoft.AspNetCore.HttpOverrides.ForwardedHeadersMiddleware: Unknown proxy
Solution:
Azure 인프라에서 전달된 헤더를 허용하도록 알려진 네트워크 및 프록시를 지웁니다.
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
// Clear known networks to accept from any proxy
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
// Or explicitly add Azure IP ranges (more secure but complex)
// options.KnownProxies.Add(IPAddress.Parse("20.x.x.x"));
});
문제: APIM은 401을 반환하지만 백 엔드는 200을 반환합니다.
증상:
- 토큰은 백 엔드에 유효합니다.
- APIM
validate-jwt정책 실패
Solution:
APIM 정책 대상이 토큰 대상과 일치하는지 확인합니다.
<validate-jwt header-name="Authorization">
<openid-config url="https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration" />
<audiences>
<!-- Must match the 'aud' claim in your token -->
<audience>api://your-backend-api-client-id</audience>
</audiences>
</validate-jwt>
문제: 여러 인증 체계 충돌
증상:
- JWT 전달자 토큰 및 기타 스키마 사용
- 잘못된 구성표가 선택됨
Solution:
컨트롤러에서 인증 체계를 명시적으로 지정합니다.
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
.AddScheme<MyCustomOptions, MyCustomHandler>("CustomScheme", options => {});
// In controller, specify scheme explicitly
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class WeatherForecastController : ControllerBase
{
// ...
}
모범 사례 준수
이러한 사례를 적용하여 게이트웨이 뒤에서 안전하고 복원력 있는 API 배포를 빌드합니다.
1. 심층 방어
게이트웨이에서 유효성을 검사하는 경우에도 항상 백 엔드 API에서 토큰의 유효성을 검사합니다.
// Gateway validates token (APIM policy)
// Backend ALSO validates token (Microsoft.Identity.Web)
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
게이트웨이 구성은 변경될 수 있으며 토큰을 재생할 수 있습니다. 심층 방어는 보안에 매우 중요합니다.
2. 게이트웨이-백 엔드 통신에 관리 ID 사용
게이트웨이가 자체 ID를 사용하여 백 엔드를 호출하는 경우 사용자 토큰과 관리 ID 토큰을 모두 수락하도록 백 엔드를 구성합니다.
// Backend accepts both user tokens and gateway's managed identity
builder.Services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters.ValidAudiences = new[]
{
"api://backend-api-client-id", // User tokens
"https://management.azure.com" // Managed identity tokens (if applicable)
};
});
3. 게이트웨이 메트릭 모니터링
게이트웨이 배포에 대한 가시성을 유지하려면 다음 주요 메트릭을 추적합니다.
- 401/403 오류율
- 토큰 유효성 검사 실패
- 건강 프로브 오류
- 전달된 헤더(디버깅용)
4. Application Insights 사용
Application Insights 원격 분석을 추가하여 게이트웨이별 요청 속성을 기록합니다.
builder.Services.AddApplicationInsightsTelemetry();
// Log custom properties
app.Use(async (context, next) =>
{
var telemetry = context.RequestServices.GetRequiredService<TelemetryClient>();
telemetry.TrackEvent("ApiRequest", new Dictionary<string, string>
{
["ForwardedFor"] = context.Request.Headers["X-Forwarded-For"],
["OriginalHost"] = context.Request.Headers["X-Forwarded-Host"],
["Gateway"] = "APIM" // or "FrontDoor", "AppGateway"
});
await next();
});
준비 상태에서 건강 상태 분리
라이브(서비스가 실행 중인가요?) 및 준비 상태(서비스에서 트래픽을 허용할 수 있나요?) 확인에 고유한 엔드포인트를 사용합니다.
// Health: Is the service running?
app.MapGet("/health", () => Results.Ok()).AllowAnonymous();
// Ready: Can the service accept traffic?
app.MapHealthChecks("/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
}).AllowAnonymous();
builder.Services.AddHealthChecks()
.AddCheck("database", () => /* check DB */ , tags: new[] { "ready" })
.AddCheck("cache", () => /* check cache */ , tags: new[] { "ready" });
6. 게이트웨이 구성 문서화
README 또는 위키 페이지를 만들어 다음을 문서화하세요.
- 사용 중인 게이트웨이
- 토큰 대상 그룹 기대치
- CORS 구성
- 헬스 프로브 엔드포인트
- 전달된 헤더 구성
- 긴급 롤백 절차
Azure API Management 사용하여 전체 예제 빌드
이 섹션에서는 Microsoft Entra ID 인증을 사용하여 Azure API Management 뒤에 있는 ASP.NET Core API의 전체 프로덕션 준비 예제를 제공합니다.
백 엔드 API(ASP.NET Core)
다음 Program.cs는 전달된 헤더, Microsoft Entra 인증, 상태 검사 및 Application Insights를 구성합니다.
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
// Forwarded headers for APIM
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.All;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
// Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph()
.AddInMemoryTokenCaches();
// Application Insights
builder.Services.AddApplicationInsightsTelemetry();
// Health checks
builder.Services.AddHealthChecks();
builder.Services.AddControllers();
var app = builder.Build();
// Health endpoint (unauthenticated)
app.MapHealthChecks("/health").AllowAnonymous();
// Middleware order is critical
app.UseForwardedHeaders();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
다음 Microsoft Entra 및 Application Insights 구성을 appsettings.json에 추가하십시오.
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "backend-api-client-id",
"Audience": "api://backend-api-client-id"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.Identity.Web": "Debug"
}
},
"ApplicationInsights": {
"ConnectionString": "your-connection-string"
}
}
다음 컨트롤러에는 디버깅을 위해 인증 및 로그 전달 헤더가 필요합니다.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web.Resource;
[Authorize]
[ApiController]
[Route("[controller]")]
[RequiredScope("access_as_user")]
public class WeatherForecastController : ControllerBase
{
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult Get()
{
// Log forwarded headers for debugging
var forwardedFor = HttpContext.Request.Headers["X-Forwarded-For"];
var forwardedHost = HttpContext.Request.Headers["X-Forwarded-Host"];
_logger.LogInformation(
"Request from {ForwardedFor} via {ForwardedHost}",
forwardedFor,
forwardedHost);
return Ok(new[] { "Weather", "Forecast", "Data" });
}
}
APIM 구성
다음 인바운드 정책은 JWT 토큰의 유효성을 검사하고, 속도 제한을 적용하고, 헤더를 전달하고, CORS를 구성합니다.
<policies>
<inbound>
<base />
<!-- Rate limiting per subscription -->
<rate-limit-by-key calls="100" renewal-period="60"
counter-key="@(context.Subscription.Id)" />
<!-- Validate JWT -->
<validate-jwt header-name="Authorization"
failed-validation-httpcode="401"
failed-validation-error-message="Unauthorized">
<openid-config url="https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration" />
<audiences>
<audience>api://backend-api-client-id</audience>
</audiences>
<issuers>
<issuer>https://login.microsoftonline.com/{tenant-id}/v2.0</issuer>
</issuers>
<required-claims>
<claim name="scp" match="any">
<value>access_as_user</value>
</claim>
</required-claims>
</validate-jwt>
<!-- Forward headers -->
<set-header name="X-Forwarded-Host" exists-action="override">
<value>@(context.Request.OriginalUrl.Host)</value>
</set-header>
<set-header name="X-Forwarded-Proto" exists-action="override">
<value>@(context.Request.OriginalUrl.Scheme)</value>
</set-header>
<!-- Backend URL -->
<set-backend-service base-url="https://your-backend.azurewebsites.net" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<!-- Add CORS headers if needed -->
<cors>
<allowed-origins>
<origin>https://your-frontend.com</origin>
</allowed-origins>
<allowed-methods>
<method>GET</method>
<method>POST</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
</cors>
</outbound>
<on-error>
<base />
</on-error>
</policies>