Distribuire API protette dietro i gateway

Distribuire API Web ASP.NET Core protette con Microsoft.Identity.Web dietro i gateway API di Azure e proxy inverso, tra cui Gestione API di Azure, Frontdoor di Azure e gateway applicazione di Azure.

Informazioni sui requisiti del gateway

Quando si distribuiscono API protette dietro i gateway, è necessario gestire diversi problemi:

  • Intestazioni inoltrate - Mantenere il contesto di richiesta originale (schema, host, IP)
  • Convalida dei token : assicurarsi che le attestazioni del gruppo di destinatari corrispondano agli URL del gateway
  • Configurazione CORS : gestire correttamente le richieste tra le origini
  • Endpoint di salute - fornisce controlli di integrità non autenticati
  • Routing basato sul percorso - Supportare i prefissi di percorso a livello di gateway
  • Terminazione SSL/TLS : gestire correttamente HTTPS quando il gateway termina SSL

Esaminare gli scenari comuni del gateway

Scegliere un gateway in base alle esigenze. Le sezioni seguenti descrivono i servizi gateway Azure più comuni per le API protette.

Gestione API di Azure

Caso d'uso: Gateway API aziendale con criteri, limitazione della frequenza, trasformazione

Architettura:

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

Considerazioni chiave:

  • Le politiche di gestione delle API possono convalidare i token JWT prima dell'inoltro al backend.
  • L'API back-end convalida ancora i token
  • L'attestazione destinatario deve corrispondere all'URL di Gestione API o all'URL backend (configurare di conseguenza)

Frontdoor di Azure

Caso d'uso: Bilanciamento del carico globale, rete CDN, protezione DDoS

Architettura:

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

Considerazioni chiave:

  • Front Door inoltra le richieste con X-Forwarded-* intestazioni
  • Terminazione SSL/TLS di Front Door
  • La convalida del gruppo di destinatari dei token richiede la configurazione

gateway applicazione di Azure

Caso d'uso: Bilanciamento del carico regionale, WAF, routing basato sul percorso

Architettura:

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

Considerazioni chiave:

  • Integrazione di Web application firewall (WAF)
  • Regole di routing basate sul percorso
  • I probe di integrità back-end necessitano di endpoint non autenticati

Configurare modelli comuni

Applicare questi modelli di configurazione per assicurarsi che l'API protetta funzioni correttamente dietro qualsiasi gateway.

1. Middleware delle intestazioni inoltrate

Configurare sempre il middleware delle intestazioni inoltrate quando si è dietro un gateway di rete. Il codice seguente registra il middleware e lo imposta per l'esecuzione prima dell'autenticazione:

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

Il middleware delle intestazioni inoltrate riveste un ruolo critico perché:

  • Mantiene l'indirizzo IP del client originale per la registrazione
  • Assicura che HttpContext.Request.Scheme rifletta lo schema HTTPS originale
  • Fornisce l'intestazione corretta Host per gli URL di reindirizzamento e la convalida dei token

2. Configurazione del gruppo di destinatari dei token

Opzione A: Accettare sia gli URL del gateway che del back-end

Aggiungere più gruppi di destinatari validi nella appsettings.json configurazione:

{
  "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"
      ]
    }
  }
}

In alternativa, configurare più gruppi di destinatari a livello di codice in 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));
    };
});

Opzione B: Riscrivere l'audience nei criteri di APIM

Configurare Gestione delle API per convalidare la dichiarazione del pubblico prima di inoltrarlo al back-end.

<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. Configurazione dell'endpoint di salute

I gateway richiedono endpoint di controllo dell'integrità non autenticati per i probe. Eseguire il mapping di un endpoint di monitoraggio prima del middleware di autenticazione per bypassare la convalida del 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();

In alternativa, usare il framework predefinito ASP.NET Core controlli di integrità per la creazione di report sull'integrità più avanzati:

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. Configurazione CORS dietro i gateway

Quando si usano Frontdoor di Azure o API Management con applicazioni frontend, configurare CORS per consentire le richieste provenienti dalle origini del gateway.

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

Importante

È necessario configurare CORS dopo le intestazioni inoltrate e prima dell'autenticazione.


Eseguire l'integrazione con Gestione API di Azure

Questa sezione fornisce la configurazione completa per la distribuzione di un'API protetta dietro Gestione API di Azure.

Configurare l'API back-end

Configurare le intestazioni inoltrate e l'autenticazione Microsoft Entra ID in 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();

Aggiungere la configurazione di Microsoft Entra a 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"
  }
}

Aggiungere una policy di ingresso in API Management per la convalida JWT

Definire un criterio in ingresso che convalida il token JWT, applica la limitazione della frequenza e inoltra la richiesta al back-end:

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

Configurare le impostazioni api di Gestione API

Usare i seguenti valori denominati e le impostazioni dell'API per completare la configurazione di APIM:

Valori denominati (per la riutilizzabilità):

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

Impostazioni API:

  • Suffisso dell'URL API: /api (prefisso del percorso opzionale)
  • URL del servizio Web: impostare tramite policy usando valori assegnati
  • Sottoscrizione obbligatoria: Sì (aggiunge un altro livello di sicurezza)

Configurare l'applicazione client

Le app client richiedono token per l'API back-end, non APIM. Il codice seguente acquisisce un token e chiama l'API tramite l'endpoint 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");

Integrazione con Frontdoor di Azure

Configurare l'API protetta per la distribuzione globale dietro Frontdoor di Azure.

Configurare l'API back-end

Configurare le intestazioni inoltrate per Frontdoor di Azure in 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();

Configurare le origini di Frontdoor

Completare i passaggi seguenti nel portale di Azure per configurare l'origine di Frontdoor:

  1. Creare un profilo Front Door
  2. Aggiungere un gruppo di origine con le istanze dell'API back-end
  3. Configurare le sonde di salute per l'endpoint /health
  4. Impostare solo l'inoltro HTTPS
  5. Abilitare i criteri WAF (facoltativo)

Impostazioni della sonda di monitoraggio:

  • Percorso: /health
  • Protocollo: HTTPS
  • Metodo: GET
  • Intervallo: 30 secondi

Gestire più aree

Quando si effettua la distribuzione in più aree dietro Front Door, aggiungere la consapevolezza delle regioni per la registrazione e la diagnostica.

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

Convalidare i token con Frontdoor

Se i client richiedono token con ambito all'URL di Frontdoor, aggiungerli all'elenco di gruppi di destinatari validi:

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

Eseguire l'integrazione con il gateway applicazione di Azure

Configurare l'API protetta dietro gateway applicazione di Azure con il supporto di Web application firewall (WAF).

Configurare l'API back-end

Configurare le intestazioni inoltrate per il gateway delle applicazioni in 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();

Configurare le impostazioni del gateway dell'applicazione

Impostare le seguenti impostazioni di backend, sonda di integrità e WAF nel portale di Azure:

Impostazioni back-end:

  • Protocollo: HTTPS (scelta consigliata) o HTTP
  • Port: 443 o 80
  • Sovrascrivere il percorso back-end: No (a meno che necessario)
  • Probe personalizzato: Sì, puntando a /health

Sonda di integrità:

  • Protocollo: HTTPS o HTTP
  • Host: lasciare l'impostazione predefinita o specificare
  • Percorso: /health
  • Intervallo: 30 secondi
  • Soglia di allerta: 3

Politica WAF:

  • Abilitare WAF con il set di regole OWASP 3.2
  • Importante: assicurarsi che i token JWT nelle Authorization intestazioni non siano bloccati
  • Potrebbe essere necessario creare esclusioni WAF per RequestHeaderNames contenere "Autorizzazione"

Configurare il routing basato sul percorso

Quando si usano regole di routing basate sul percorso, configurare l'API back-end per gestire il prefisso del percorso:

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

Regola del gateway delle applicazioni:

  • Percorso: /api/v1/*
  • Destinazione backend: pool backend
  • Impostazioni back-end: usare le impostazioni configurate

Risolvere i problemi comuni

Usare queste soluzioni per risolvere i problemi più comuni durante la distribuzione di API protette dietro i gateway.

Problema: 401 Non autorizzato dopo la distribuzione dietro a un gateway

Sintomi:

  • L'API funziona in locale ma restituisce un errore 401 dietro il gateway.
  • Il token sembra valido quando decodificato in jwt.ms

Possibili cause:

  1. Mancata corrispondenza della dichiarazione dei destinatari

    # 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 per intestazioni inoltrate mancanti

    // Ensure this is BEFORE authentication
    app.UseForwardedHeaders();
    app.UseAuthentication();
    
  3. Problemi di reindirizzamento HTTPS

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

Soluzione:

  • Abilitare la registrazione di debug per visualizzare i dettagli di convalida dei token
  • Aggiungere più gruppi di destinatari validi nella convalida del token
  • Verificare che le intestazioni X-Forwarded-* vengano inoltrate dal gateway

Problema: i probe di integrità hanno esito negativo

Sintomi:

  • Il gateway contrassegna il back-end come non funzionante
  • L'endpoint di integrità restituisce 401

Soluzione:

Verificare che l'endpoint di integrità venga eseguito prima del middleware di autenticazione:

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

Problema: errori CORS dietro Frontdoor

Sintomi:

  • Le richieste OPTIONS preliminari hanno esito negativo
  • La console del browser mostra gli errori CORS

Soluzione:

Aggiungere le origini frontdoor e front-end ai criteri 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();

Problema: avvisi sull'intestazione inoltrata nei log

Sintomi:

Microsoft.AspNetCore.HttpOverrides.ForwardedHeadersMiddleware: Unknown proxy

Soluzione:

Reimpostare le configurazioni delle reti note e dei proxy per accettare le intestazioni inoltrate dall'infrastruttura di 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"));
});

Problema: APIM restituisce 401 ma il backend restituisce 200

Sintomi:

  • Il token è valido per il back-end
  • Il criterio di Gestione API validate-jwt fallisce

Soluzione:

Verificare che il gruppo di destinatari dei criteri di Gestione API corrisponda al gruppo di destinatari del 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>

Problema: conflitti tra più schemi di autenticazione

Sintomi:

  • Uso sia del bearer JWT che di altri schemi
  • Schema errato selezionato

Soluzione:

Specificare lo schema di autenticazione in modo esplicito nel controller:

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

Seguire le migliori pratiche

Applicare queste procedure per creare una distribuzione api sicura e resiliente dietro i gateway.

1. Difesa in profondità

Convalidare sempre i token nell'API back-end, anche se il gateway li convalida:

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

La configurazione del gateway può essere modificata e i token possono essere riprodotti. La difesa avanzata è fondamentale per la sicurezza.

2. Usare le identità gestite per la comunicazione da gateway a back-end

Se il gateway chiama il back-end con la propria identità, configurare il back-end per accettare sia token utente che token di identità gestiti:

// 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. Monitorare le metriche del gateway

Tenere traccia di queste metriche chiave per mantenere la visibilità sulla distribuzione del gateway:

  • 401/403 percentuali di errore
  • Errori di convalida dei token
  • Errori della sonda di integrità
  • Intestazioni inoltrate (per il debugging)

4. Utilizzare Application Insights

Aggiungere i dati di telemetria di Application Insights per registrare le proprietà delle richieste specifiche del 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();
});

5. Separare la salute da disponibile

Usare endpoint distinti per l'attività (è il servizio in esecuzione?) e la prontezza (il servizio può accettare il traffico?) verifiche:

// 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. Documentare la configurazione del gateway

Creare una pagina README o wiki che documenta:

  • Quali gateway sono in uso
  • Aspettative dei destinatari dei token
  • Configurazione CORS
  • Endpoint delle probe di stato
  • Configurazione delle intestazioni inoltrate
  • Procedure di rollback di emergenza

Creare un esempio completo con Gestione API di Azure

Questa sezione fornisce un esempio completo e pronto per la produzione di un'API ASP.NET Core dietro Gestione API di Azure con autenticazione Microsoft Entra ID.

Back-end API (ASP.NET Core)

L'Program.cs seguente configura le intestazioni inoltrate, l'autenticazione Microsoft Entra, i controlli di integrità e 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();

Aggiungere la configurazione di Microsoft Entra e di Application Insights seguente in 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"
  }
}

Il seguente controller richiede l'autenticazione e registra le intestazioni inoltrate per il debug.

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

Configurazione di API Management

La politica di ricezione seguente convalida i token JWT, applica la limitazione della frequenza, inoltra le intestazioni e configura 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>