部署使用 Microsoft.Identity.Web 保護的 ASP.NET Core 網路 API,這些 API 部署在 Azure API 閘道和反向代理後面,包括 Azure API 管理(APIM)、Azure Front Door 和 Azure 應用程式閘道。
了解閘道需求
當你將受保護的 API 部署在閘道後時,必須處理以下幾項考量:
- 轉發標頭 - 保留原始請求上下文(方案、主機、IP)
- 令牌驗證 - 確保受眾聲明與閘道網址相符
- CORS 設定 - 正確處理跨來源請求
- 健康端點 - 提供未經認證的健康檢查
- 基於路徑的路由 - 支援閘道層級路徑前綴
- SSL/TLS 終止 - 當閘道終止 SSL 時,正確處理 HTTPS
檢視常見閘道情境
根據你的需求選擇閘道器。 以下章節將介紹最常見的 Azure 閘道服務,針對受保護的 API。
Azure API 管理 (APIM)
使用情境: 企業 API 閘道器,具備政策、速率限制與轉換功能
架構:
Client → Microsoft Entra ID → Token
Client → APIM (apim.azure-api.net) → Backend API (app.azurewebsites.net)
主要考慮因素:
- APIM 政策可以在轉發到後端前驗證 JWT 代幣
- 後端 API 仍然會驗證 tokens
- 受眾聲明必須與 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-*標頭的請求 - 在前門終止 SSL/TLS
- 憑證受眾驗證需要設定
Azure 應用程式閘道
使用情境: 區域負載平衡、WAF、基於路徑的路由
架構:
Client → Microsoft Entra ID → Token
Client → Application Gateway → Backend API (multiple instances)
主要考慮因素:
- 網路應用防火牆(WAF)整合
- 基於路徑的路由規則
- 後端健康探測器需要未認證的端點
配置常見模式
套用這些設定模式,確保你的受保護 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 協議 - 提供正確的
Host標頭以用於重定向 URL 及令牌驗證
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();
重要
CORS 必須在轉發標頭 之後 、認證 前 設定。
與 Azure API 管理整合
本節提供部署 Azure API 管理 後保護 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();
將 Microsoft Entra 的配置加入至 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"
}
}
新增 APIM 入站策略以進行 JWT 驗證
定義一個入站政策,驗證 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 租戶識別碼 -
backend-api-client-id: 後端 API 的客戶端 ID -
backend-base-url:https://your-backend.azurewebsites.net
API 設定:
-
API URL 後綴:
/api(可選路徑前綴) - 網路服務網址:透過使用命名值的政策設定
- 需訂閱:是的(增加另一層安全措施)
設定用戶端應用程式
客戶端應用程式會請求 後端 API 的權杖,而不是 APIM。 以下程式碼會取得一個令牌,並透過 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();
設定前門起點
請在 Azure 入口網站完成以下步驟以設定 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 應用閘道整合
在 Azure 應用程式閘道 上配置受 Web 應用程式防火牆(WAF)保護的 API。
設定後端 API
在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();
配置應用程式閘道設定
在 Azure 入口網站中設定以下後端、健康探測與 WAF 設定:
後端設定:
- 協定:HTTPS(建議)或 HTTP
- 端口:443 或 80
- 覆寫後端路徑:無(除非必要)
-
自訂探針:是的,指向
/health
健康探測:
- 協定:HTTPS 或 HTTP
- 主機:保留原始設定或自行指定
-
路徑:
/health - 中場休息:30秒
- 不健康的閾值:3
WAF 政策:
- 啟用 Web 應用程式防火牆 (WAF) 及 OWASP 3.2 規則集
-
重要事項:確保標頭中的
AuthorizationJWT 標記不會被阻塞 - 你可能需要為
RequestHeaderNames包含「授權」的項目建立 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();
應用閘道規則:
-
路徑:
/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-*閘道器是否將標頭轉發
問題:健康探針失效
徵兆:
- Gateway 將後端標記為不健康
- 健康端點回傳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
問題:前門後方的 CORS 錯誤
徵兆:
- 檢查前選項請求失敗
- 瀏覽器主控台顯示 CORS 錯誤
Solution:
將您的 Front Door 和前端來源添加到 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();
問題:「轉送標頭」警告出現在日誌中
徵兆:
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
徵兆:
- Token 對後端有效
- 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 Bearer 和其他身份驗證方案
- 選錯方案
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. 使用受管理身份進行閘道到後端通訊
如果你的閘道器用自己的身份呼叫後端,請設定後端同時接受使用者權杖和管理身份權杖:
// 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 遙測以記錄閘道特定請求屬性:
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. 將健康與準備狀態分開
在現場狀態(服務是否正在運行?)和準備狀態(服務能否接受流量?)檢查時,使用不同的端點:
// 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 管理建立完整範例
本節提供完整且可生產環境的範例,展示 Azure API 管理 背後的 ASP.NET Core API 與 Microsoft Entra ID 認證。
後端 API(ASP.NET Core)
以下 Program.cs 配置轉發標頭、Microsoft Entra 身份驗證、健康檢查,以及應用程式洞察:
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>