我正在测试一个自托管的 Asp Net Core Web 服务器(Kestrel),并且我正在努力使用自签名证书进行客户端身份验证。 这是我的启动代码
WebApplicationBuilder webBuilder = WebApplication.CreateBuilder();
var webHostBuilder = builder.WebHost;
X509Certificate2 rootCert = new X509Certificate2(hostCertFilePath, hostCertPassword);
webHostBuilder.ConfigureKestrel(o =>
{
o.ConfigureHttpsDefaults(o =>
{
o.ServerCertificate = rootCert;
o.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
});
});
webHostBuilder.UseKestrel(o =>
{
o.Listen(IPAddress.Parse(myHttpsEndPointIpAddr), myHttpsEndPointPort,
listenOptions =>
{
listenOptions.UseHttps();
});
o.Listen(IPAddress.Parse(myHttpEndPointIpAddr), myHttpEndPointPort);
});
var services = webBuilder.Services;
services.AddTransient<MyCustomCertificateValidationService>();
services
.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.AllowedCertificateTypes = CertificateTypes.SelfSigned;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService = context.HttpContext.RequestServices
.GetService<MyCustomCertificateValidationService>();
if (validationService.ValidateCertificate(context.ClientCertificate))
{
context.Success();
}
else
{
context.Fail("invalid cert");
}
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
context.Fail("invalid cert");
return Task.CompletedTask;
}
};
});
...
var app = webBuilder.Build();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
这是我的定制认证课程
public class MyCustomCertificateValidationService
{
public bool ValidateCertificate(X509Certificate2 clientCertificate)
{
// todo: check certificate thumbnail
return false;
}
}
但是,即使 MyCustomCertificateValidationService 有一个返回 false 的方法 ValidateCertificate(),当客户端通过控制器方法的路由访问 url 时,仍然会调用控制器方法。 这是日志中显示的内容:
...
AspNetCore.Routing.EndpointRoutingMiddleware : Request matched endpoint ‘GetMyData…‘
AspNetCore.Authentication.Certificate.CertificateAuthenticationHandler : Certificate was not authenticated. Failure message: invalid cert
AspNetCore.Routing.EndpointMiddleware : Executing endpoint ‘GetMyData…‘
...
知道为什么控制器方法仍然被调用吗?
“该应用程序有一个用例,在某些测试环境中 还应该允许未经授权的调用(通过 http://...)。我会 如果可能的话,更喜欢使用设置参数来动态决定 如果允许或不允许 http 访问,而不是将其“硬编码”为 [授权]属性”
当然可以。当然,有一种方便的方法可以使用middleware来实现您的需求。请尝试下面的代码片段:
Http/Https请求中间件基于环境:
public class CustomHttpHttpsRequestMiddleware
{
private readonly RequestDelegate next;
public CustomHttpHttpsRequestMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
//env = "Production";
if (env == "Development")
{
await next(context);
}
else
{
if (!context.Request.IsHttps)
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
await context.Response.WriteAsync("HTTPS required!");
}
}
}
}
注意:在应用程序中
request context
我们检查两个重要的值,首先如果请求是安全的意味着cIsHttps
和application environment
,在Development
环境中我们将允许http
请求。因此,除了dev
或任何基于我们要求的env
,我们将拒绝http
请求。
在Program.cs上注册中间件:
app.UseMiddleware<CustomHttpHttpsRequestMiddleware>();
注意: 确保您遵循了正确的中间件顺序。为了避免短路,您可以将此中间件放在当前所有中间件的下方。
输出:
您必须在 appsettings.json
中仅配置 HTTPS{
"Kestrel": {
"EndPoints": {
"Https": {
"Url": "https://<IP>:<PORT>"
}
}
}
}
另外,需要在 Program.cs
中强制执行 HTTPSapp.UseHttpsRedirection();