Menyebarkan API yang dilindungi di belakang gateway

Sebarkan API web ASP.NET Core yang dilindungi dengan Microsoft. Identity.Web di belakang gateway API Azure dan proksi terbalik, termasuk Azure API Management (APIM), Azure Front Door, dan Azure Application Gateway.

Memahami persyaratan gateway

Saat Anda menyebarkan API yang dilindungi di belakang gateway, Anda harus menangani beberapa masalah:

  • Header yang diteruskan - Mempertahankan konteks permintaan asli (skema, host, IP)
  • Validasi token - Pastikan klaim audiens cocok dengan URL gateway
  • Konfigurasi CORS - Menangani permintaan lintas asal dengan benar
  • Endpoint kesehatan - Memberikan pemeriksaan kesehatan yang tidak diautentikasi
  • Routing berbasis jalur - Mendukung awalan jalur pada tingkat gateway
  • Penghentian SSL/TLS - Tangani HTTPS dengan benar saat gateway mengakhiri SSL

Meninjau skenario gateway umum

Pilih gateway berdasarkan kebutuhan Anda. Bagian berikut ini menjelaskan layanan gateway Azure yang paling umum untuk API yang dilindungi.

Azure API Management (APIM)

Kasus penggunaan: Gateway API Perusahaan dengan kebijakan, pembatasan tarif, transformasi

Arsitektur:

Client → Microsoft Entra ID → Token
Client → APIM (apim.azure-api.net) → Backend API (app.azurewebsites.net)

Pertimbangan utama:

  • Kebijakan APIM dapat memvalidasi token JWT sebelum meneruskan ke backend
  • API backend masih memvalidasi token
  • Klaim audiens harus cocok dengan URL APIM atau URL backend (dikustomisasi sesuai kebutuhan)

Azure Front Door

Kasus penggunaan: Penyeimbangan beban global, CDN, perlindungan DDoS

Arsitektur:

Client → Microsoft Entra ID → Token
Client → Front Door (azurefd.net) → Backend API (regional endpoints)

Pertimbangan utama:

  • Front Door meneruskan permintaan dengan X-Forwarded-* header
  • Penghentian SSL/TLS di Front Door
  • Konfigurasi untuk validasi audiens token diperlukan

Azure Application Gateway

Kasus penggunaan: Penyeimbangan beban regional, WAF, perutean berdasarkan jalur

Arsitektur:

Client → Microsoft Entra ID → Token
Client → Application Gateway → Backend API (multiple instances)

Pertimbangan utama:

  • Integrasi dengan Web Application Firewall (WAF)
  • Aturan perutean berbasis jalur
  • Pemeriksaan kesehatan backend menggunakan endpoint yang tidak diautentikasi.

Mengonfigurasi pola-pola umum

Terapkan pola konfigurasi ini untuk memastikan API yang dilindungi berfungsi dengan benar di belakang gateway apa pun.

1. Middleware header yang diteruskan

Selalu konfigurasikan middleware "forwarded headers" ketika berada di belakang gateway. Kode berikut mendaftarkan middleware dan mengaturnya untuk dijalankan sebelum autentikasi:

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

Middleware header yang diteruskan sangat penting karena:

  • Mempertahankan alamat IP klien asli untuk pengelogan
  • HttpContext.Request.Scheme Memastikan mencerminkan skema HTTPS asli
  • Menyediakan header yang benar Host untuk URL pengalihan dan validasi token

2. Konfigurasi audiens token

Opsi A: Terima kedua URL gateway dan backend

Tambahkan beberapa audiens yang valid dalam konfigurasi Anda 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"
      ]
    }
  }
}

Atau, konfigurasikan beberapa audiens secara terprogram di 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));
    };
});

Opsi B: Menulis ulang audiens dalam kebijakan APIM

Konfigurasikan APIM untuk memvalidasi klaim audiens sebelum meneruskan ke backend:

<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. Konfigurasi titik akhir kesehatan

Gateway memerlukan titik akhir kesehatan yang tidak diautentikasi untuk pemeriksaan. Petakan titik akhir kesehatan sebelum middleware autentikasi untuk melewati validasi token:

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

Atau, gunakan kerangka kerja Pemeriksaan Kesehatan ASP.NET Core bawaan untuk pelaporan kesehatan yang lebih kaya:

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. Konfigurasi CORS di belakang gateway

Saat Anda menggunakan Azure Front Door atau APIM dengan aplikasi frontend, konfigurasikan CORS untuk mengizinkan permintaan dari asal gateway Anda:

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

Penting

CORS harus dikonfigurasi setelah header yang diteruskan dan sebelum autentikasi.


Integrasikan dengan Azure API Management

Bagian ini menyediakan konfigurasi lengkap untuk menyebarkan API yang dilindungi di belakang Azure API Management.

Mengonfigurasi API backend

Siapkan header yang diteruskan dan autentikasi Microsoft Entra ID di Program.cs:

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

Tambahkan konfigurasi Microsoft Entra ke appsettings.json:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-backend-api-client-id",
    "Audience": "api://your-backend-api-client-id"
  }
}

Menambahkan kebijakan masuk APIM untuk validasi JWT

Tentukan kebijakan masuk yang memvalidasi token JWT, menerapkan pembatasan tarif, dan meneruskan permintaan ke backend:

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

Mengonfigurasi pengaturan APIM API

Gunakan nilai bernama dan pengaturan API berikut untuk menyelesaikan konfigurasi APIM:

Nilai Bernama (untuk kemudahan penggunaan ulang):

  • tenant-id: ID penyewa Microsoft Entra Anda
  • backend-api-client-id: ID klien Backend API
  • backend-base-url: https://your-backend.azurewebsites.net

Pengaturan API:

  • Akhiran URL API: /api (awalan jalur opsional)
  • URL layanan web: Diatur melalui kebijakan dengan nilai yang telah diberi nama
  • Langganan diperlukan: Ya (menambahkan lapisan keamanan lain)

Mengonfigurasi aplikasi klien

Aplikasi klien meminta token untuk API backend, bukan APIM. Kode berikut memperoleh token dan memanggil API melalui titik akhir APIM:

// 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");

Integrasikan dengan Azure Front Door

Konfigurasikan API yang dilindungi untuk distribusi global di belakang Azure Front Door.

Mengonfigurasi API back-end

Siapkan header yang diteruskan untuk Azure Front Door di Program.cs:

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

Mengonfigurasi sumber Front Door

Selesaikan langkah-langkah berikut di portal Azure untuk menyiapkan asal Front Door:

  1. Membuat profil Front Door
  2. Tambahkan grup asal dengan instance API backend Anda
  3. Mengonfigurasi pemeriksaan kesehatan pada /health titik akhir
  4. Mengatur penerusan hanya HTTPS
  5. Mengaktifkan kebijakan WAF (opsional)

Pengaturan Health Probe:

  • Jalur: /health
  • Protokol: HTTPS
  • Metode: GET
  • Interval: 30 detik

Menangani beberapa wilayah

Saat Anda menyebarkan ke beberapa wilayah di belakang Front Door, tambahkan kesadaran wilayah untuk pengelogan dan diagnostik:

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

Memvalidasi token dengan Front Door

Jika klien meminta token yang dibatasi pada URL Front Door, tambahkan ke daftar audiens yang valid.

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

Mengintegrasikan dengan Azure Application Gateway

Konfigurasikan API yang dilindungi di belakang Azure Application Gateway dengan dukungan Web Application Firewall (WAF).

Mengonfigurasi API backend

Siapkan header yang diteruskan untuk Application Gateway di 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();

Mengonfigurasi pengaturan Application Gateway

Atur pengaturan berikut untuk backend, pemeriksaan kesehatan, dan WAF di portal Azure:

Pengaturan Backend:

  • Protokol: HTTPS (disarankan) atau HTTP
  • Port: 443 atau 80
  • Mengesampingkan jalur backend: Tidak (kecuali diperlukan)
  • Peninjau kustom: Ya, menunjuk ke /health

Sonde Kesehatan:

  • Protokol: HTTPS atau HTTP
  • Host: Biarkan default atau tentukan
  • Jalur: /health
  • Interval: 30 detik
  • Ambang tidak sehat: 3

Kebijakan WAF:

  • Mengaktifkan WAF dengan ruleset OWASP 3.2
  • Penting: Pastikan token JWT di Authorization header tidak diblokir
  • Anda mungkin perlu membuat pengecualian WAF untuk RequestHeaderNames berisi "Otorisasi"

Menyiapkan perutean berbasis jalur

Saat Anda menggunakan aturan perutean berbasis jalur, konfigurasikan API backend Anda untuk menangani awalan jalur:

// 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();

Aturan Gerbang Aplikasi:

  • Jalur: /api/v1/*
  • Target backend: Kumpulan backend Anda
  • Pengaturan backend: Gunakan pengaturan yang dikonfigurasi

Pemecahan Masalah Umum

Gunakan solusi ini untuk mengatasi masalah paling umum saat menyebarkan API yang dilindungi di belakang gateway.

Masalah: 401 Tidak Sah setelah implementasi di balik gateway

Gejala:

  • API berfungsi secara lokal tetapi mengembalikan 401 di belakang gateway
  • Token tampaknya valid ketika didekodekan pada jwt.ms

Kemungkinan penyebabnya:

  1. Ketidakcocokan klaim audiens

    # 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
    
  2. Middleware header yang diteruskan hilang

    // Ensure this is BEFORE authentication
    app.UseForwardedHeaders();
    app.UseAuthentication();
    
  3. Masalah pengalihan HTTPS

    // If gateway terminates SSL, may need to disable or configure carefully
    if (!app.Environment.IsDevelopment())
    {
        app.UseHttpsRedirection();
    }
    

Solution:

  • Mengaktifkan pengelogan debug untuk melihat detail validasi token
  • Menambahkan beberapa audiens yang valid dalam validasi token
  • Verifikasi bahwa X-Forwarded-* header diteruskan oleh gateway

Masalah: Pemeriksaan kesehatan gagal

Gejala:

  • Gateway menandai backend sebagai tidak sehat
  • Titik akhir kesehatan mengembalikan 401

Solution:

Pastikan endpoint kesehatan dijalankan sebelum middleware autentikasi:

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

Masalah: Kesalahan CORS di belakang Front Door

Gejala:

  • Permintaan OPSI Preflight gagal
  • Konsol browser menunjukkan kesalahan CORS

Solution:

Tambahkan Front Door dan asal frontend Anda ke kebijakan CORS:

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

Masalah: Peringatan "Header yang diteruskan" dalam log

Gejala:

Microsoft.AspNetCore.HttpOverrides.ForwardedHeadersMiddleware: Unknown proxy

Solution:

Hapus jaringan dan proksi yang diketahui untuk menerima header yang diteruskan dari infrastruktur 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"));
});

Masalah: APIM mengembalikan 401 tetapi backend mengembalikan 200

Gejala:

  • Token sah untuk backend
  • Kebijakan APIM validate-jwt gagal

Solution:

Pastikan audiens kebijakan APIM sesuai dengan audiens token.

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

Masalah: Konflik beberapa skema autentikasi

Gejala:

  • Menggunakan token JWT dan skema lainnya
  • Skema yang salah dipilih

Solution:

Tentukan skema autentikasi secara eksplisit di pengontrol:

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
{
    // ...
}

Ikuti praktik terbaik

Terapkan praktik ini untuk membangun penyebaran API yang aman dan tangguh di belakang gateway.

1. Pertahanan secara mendalam

Selalu validasi token di API backend, meskipun gateway memvalidasinya:

// Gateway validates token (APIM policy)
// Backend ALSO validates token (Microsoft.Identity.Web)
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

Konfigurasi gateway dapat berubah dan token dapat diputar ulang. Pertahanan secara mendalam sangat penting untuk keamanan.

2. Gunakan identitas terkelola untuk komunikasi gateway-ke-backend

Jika gateway Anda memanggil backend dengan identitasnya sendiri, konfigurasikan backend untuk menerima token pengguna dan token identitas terkelola:

// 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. Memantau metrik gerbang

Lacak metrik utama ini untuk mempertahankan visibilitas dalam implementasi gateway Anda:

  • Tingkat kesalahan 401/403
  • Kegagalan validasi token
  • Kegagalan sensor kesehatan
  • Header yang diteruskan (untuk debugging)

4. Gunakan Application Insights

Tambahkan telemetri Application Insights untuk mencatat properti permintaan khusus milik gateway.

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

Pisahkan status kesehatan dari status siap

Gunakan titik akhir yang berbeda untuk keaktifan (apakah layanan berjalan?) dan kesiapan (dapatkah layanan menerima lalu lintas?) pemeriksaan:

// 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. Dokumentasikan konfigurasi gateway Anda

Buat halaman README atau wiki yang mencakup:

  • Gateway mana yang sedang digunakan
  • Harapan audiens terhadap token
  • Konfigurasi CORS
  • Titik akhir pemeriksaan kesehatan
  • Konfigurasi header yang diteruskan
  • Prosedur putar kembali darurat

Buat contoh lengkap dengan Azure API Management

Bagian ini menyediakan contoh lengkap dan siap produksi dari API ASP.NET Core di belakang Azure API Management dengan autentikasi Microsoft Entra ID.

API Backend (ASP.NET Core)

Program.cs berikut mengatur pengalihan header, autentikasi Microsoft Entra, pengecekan kesehatan sistem, dan 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();

Tambahkan konfigurasi Microsoft Entra dan Application Insights berikut ke 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"
  }
}

Pengontrol berikut memerlukan header autentikasi dan log yang diteruskan untuk penelusuran kesalahan:

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

Konfigurasi APIM

Kebijakan masuk berikut memvalidasi token JWT, menerapkan pembatasan tarif, meneruskan header, dan mengonfigurasi 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>