将受Microsoft.Identity.Web保护的ASP.NET Core Web API部署在Azure API网关和反向代理之后,包括Azure API 管理(APIM)、Azure Front Door和Azure 应用程序网关。
了解网关要求
在网关后面部署受保护的 API 时,必须处理以下几个问题:
- 转发标头 - 保留原始请求上下文(方案、主机、IP)
- 令牌验证 - 确保访问群体声明与网关 URL 匹配
- CORS 配置 - 正确处理跨域请求
- 健康检查终点 - 提供未经身份验证的健康检查
- 基于路径的路由 - 支持网关级路径前缀
- SSL/TLS 终止 - 网关终止 SSL 时正确处理 HTTPS
查看常见网关场景
根据要求选择网关。 以下各节介绍受保护 API 的最常见Azure网关服务。
Azure API 管理 (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 应用程序网关
用例: 区域负载均衡、WAF、基于路径的路由
体系结构:
Client → Microsoft Entra ID → Token
Client → Application Gateway → Backend API (multiple instances)
关键注意事项:
- Web 应用程序防火墙 (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 协议 - 为重定向 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();
重要
必须在转发标头 之后 和身份验证 之前 配置 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"
}
}
为 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(可选路径前缀) - Web 服务 URL:通过策略使用命名值进行设置
- 需要订阅:是(添加另一层安全性)
配置客户端应用程序
客户端应用请求 后端 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 集成
配置受保护的 API,通过 Azure Front Door 实现全球分发。
配置后端 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 门户中完成以下步骤以设置 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 应用程序网关集成
使用 Web 应用程序防火墙 (WAF) 支持在 Azure 应用程序网关 后面配置受保护的 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 策略:
- 使用 OWASP 3.2 规则集启用 WAF
-
重要说明:确保不会阻止标头中的
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-*标头是否由网关转发
问题:健康探测失败
症状:
- 网关将后端标记为不正常
- 运行状况终结点返回 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
问题:Azure Front Door 后的 CORS 错误
症状:
- 预检 OPTIONS 请求失败
- 浏览器控制台显示 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
症状:
- 令牌对后端有效
- 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.使用托管标识进行网关到后端通信
如果网关使用自己的标识调用后端,请将后端配置为接受用户令牌和托管标识令牌:
// 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();
});
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 或 Wiki 页面,记录以下内容:
- 正在使用哪个网关
- 令牌用户期望
- CORS 配置
- 运行状况探测端点
- 转发标头配置
- 紧急回滚程序
使用 Azure API 管理 生成完整的示例
本部分提供了一个在Azure API 管理后面运行,并使用Microsoft Entra ID进行身份验证的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>