配置ASP.NET Core使用反向代理

当请求经过代理服务器、负载均衡器中转到达应用服务器时,请求的scheme(HTTP or HTTPS)、Host、端口以及原始客户端 IP 地址已经经过改变,原始信息在标头中转接,这些信息在一些请求处理中很重要,例如在重定向、身份验证、链接生成、策略评估和客户端地理位置中。
ForwardedHeadersMiddleware中间件将读取包含原始请求信息的标头并填充 HttpContext 上的关联字段来恢复正确的原始请求信息,以便后续的中间件像处理普通请求一样处理经过转发的请求。
代理转接 HTTP 标头主要有以下几种:
| 标题 | 说明 |
|---|---|
X-Forwarded-For (XFF) |
包含原始客户端和后续代理的 IP 地址以及可选端口号。如果请求经过多个代理,则该值将是列表。第一个参数表示最初发出请求的客户端,后续代理按顺序排列, 链中的最后一个代理不在参数列表中。 |
X-Forwarded-Proto (XFP) |
指示原始请求是 HTTP 还是 HTTPS。 如果请求经过多个代理,则该值将是列表,同上。 |
X-Forwarded-Host (XFH) |
包含客户端请求的原始主机。如果请求经过多个代理,则该值将是列表,同上。。 |
X-Forwarded-Prefix |
客户端请求的原始基路径。 此标头对于应用服务器正确生成 URL、重定向或回到客户端的链接十分有用。如果请求经过多个代理,则该值将是列表,同上。。 |
ForwardedHeadersMiddleware中间件将更新以下值:
| 属性 | 说明 |
|---|---|
| HttpContext.Connection.RemoteIpAddress | 更新为 X-Forwarded-For 标头值。使用的值将从 X-Forwarded-For 中删除,HttpContext.Connection.RemoteIpAddress 的旧值将保存在 X-Original-For 中。如果X-Forwarded-For是代理链,将重复以上过程,在X-Original-For构建一个反向代理链。 |
| HttpContext.Request.Scheme | 更新为 X-Forwarded-Proto 标头值。使用的值将从 X-Forwarded-Proto 中删除,HttpContext.Request.Scheme 的旧值将保存在 X-Original-Proto 中。如果是代理链将重复以上过程。 |
| HttpContext.Request.Host | 使用 X-Forwarded-Host 标头值进行设置。 使用的值将从 X-Forwarded-Host 中删除,HttpContext.Request.Host 的旧值将保存在 X-Original-Host 中。如果是代理链将重复以上过程。 |
| HttpContext.Request.PathBase | 使用 X-Forwarded-Prefix 标头值进行设置。 使用的值将从 X-Forwarded-Prefix 中删除,HttpContext.Request.PathBase 的旧值将保存在 X-Original-Prefix 中。如果是代理链将重复以上过程。 |
添加ForwardedHeadersMiddleware中间件
using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
// 配置ForwardedHeaders
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// 或设置为 ForwardedHeaders.All 以便处理所有转发标头
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseForwardedHeaders(); // 添加中间件
app.UseHsts();
}
else
{
app.UseDeveloperExceptionPage();
app.UseForwardedHeaders(); // 添加中间件
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
重要:
ForwardedHeadersMiddleware必须在调用 UseHsts 之前运行。
配置ForwardedHeadersOptions
需要配置ForwardedHeadersOptions.ForwardedHeaders属性,指定需要转发的标头。如果未配置,则默认值是ForwardedHeaders.None,将不会用转发标头更新任何属性,即ForwardedHeadersMiddleware不起任何作用。
KnownNetworks和KnownProxies的默认值为环回地址,即默认只响应回环地址代理。
默认转发标头名称为 X-Forwarded-For、X-Forwarded-Proto、X-Forwarded-Host 和 X-Forwarded-Prefix。
处理基路径
一些代理服务器可能会加上基路径,例如应用服务器路径是/api/1,而代理服务器的路径是/foo/api/1,加上了基路径/foo。通常代理转发到应用服务器时会移除基路径,并在X-Forwarded-Prefix标头中添加基路径。如前所述,ForwardedHeadersMiddleware中间件会将Request.PathBase设置为基路径/foo。
如果代理服务器没有移除基路径,并且也没有使用X-Forwarded-Prefix标头,这种情况需要使用UsePathBase中间件将Request.PathBase 设置为 /foo,将 Request.Path 改为 /api/1:
app.UsePathBase("/foo");
// ...
app.UseRouting();
UsePathBase必须在app.UseRouting之前调用,以便路由中间匹配正确的路由。当再次反向调用中间件时,将重新应用原始路径和基路径。
如果只是移除基路径,示例:
app.Use((context, next) =>
{
if (context.Request.Path.StartsWithSegments("/foo", out var remainder))
{
context.Request.Path = remainder;
}
return next(context);
});
转发客户端证书
为使用了双向TLS认证的Azure 应用服务器配置证书转发。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
// 指定客户端证书标头名称
builder.Services.AddCertificateForwarding(options =>
options.CertificateHeader = "X-ARR-ClientCert");
var app = builder.Build();
// 转发证书
app.UseCertificateForwarding();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthorization();
app.UseAuthentication();
app.MapRazorPages();
app.Run();
要在调用UseAuthentication之前调用UseCertificateForwarding。
故障排查
如果存在故障,调用UseHttpLogging中间件打印Http日志,观察经过ForwardedHeaders中间件后的请求信息。
using Microsoft.AspNetCore.HttpLogging;
using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddHttpLogging(options =>
{
options.LoggingFields = HttpLoggingFields.RequestPropertiesAndHeaders;
});
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
var app = builder.Build();
app.UseForwardedHeaders();
app.UseHttpLogging();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
必须在UseForwardedHeaders之后调用UseHttpLogging。
Http日志以Information级别显示。如果未显示,请将 "Microsoft.AspNetCore.HttpLogging": "Information" 添加到 appsettings.Development.json 文件:
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.AspNetCore.HttpLogging": "Information"
}
}
}
本文来自博客园,作者:星墨,转载请注明原文链接:https://www.cnblogs.com/yada/p/19226218

浙公网安备 33010602011771号