AuthorizationFilterAttribute 中没有命中断点

Breakpoint not getting hit in AuthorizationFilterAttribute

我有这个属性:

public class ValidateCertAttribute : AuthorizationFilterAttribute
{
    public override void OnAuthorization(HttpActionContext context)
    {
        // Read certificate from the HTTP Request and
        // check its various attributes against known
        // values from a config file.
        if (true) // certificate is invalid
        {
            context.Response = new HttpResponseMessage(HttpStatusCode.Forbidden)
            {
                ReasonPhrase = "Invalid certificate"
            };
        }
        else
        {
            base.OnAuthorization(context);
        }
    }
}

此操作:

[HttpGet]
[Route("TestAuth")]
[ValidateCert]
public HttpResponseMessage TestAuth()
{
    return new HttpResponseMessage(HttpStatusCode.OK)
    {
        ReasonPhrase = "In Test method without any authorization."
    };
}

此操作在 .NET Core Web Api 控制器中:

[ApiController]
public class TestAuthController : ControllerBase

另一方面,Startup.cs 包含:

app.UseMvc();

这看起来很奇怪,因为这应该只是一个 Web Api 控制器,而不是 MVC Web 应用程序。显然,这只是用于路由,但我认为值得一提。

我在属性代码的第一行设置了一个断点,但它没有被击中。我相信它应该在动作执行之前被击中,并且动作永远不应该执行,因为我在属性中设置了 Response 。为什么属性没有执行?

这可以使用 policy-based 授权来完成。

这个想法本质上是您有一个需要满足的要求(有效证书),一个要求的处理程序(如何验证证书)和一个强制执行此要求并在授权期间应用的策略。

如果您的代码足够简单,您只需提供一个 Func<AuthorizationHandlerContext, bool> 到申请评估的策略即可。以下是设置策略的方法(在 Startup.cs、ConfigureServices() 中):

services.AddAuthorization(options =>
{
    options.AddPolicy("ValidateCertificate", policy =>
       policy.RequireAssertion(context => 
       {
           var filterContext = (AuthorizationFilterContext)context.Resource;
           var Response = filterContext.HttpContext.Response;
           var message = Encoding.UTF8.GetBytes("Invalid certificate");
           Response.OnStarting(async () =>
           {
               filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
               await Response.Body.WriteAsync(message, 0, message.Length);
           });                       
           return false;
       }));
});

这会做你想做的事。

现在,如果你想走更结构化的路线,你可以实现每个部分:

首先,创建您的需求(更像是一个参考标记,真的):

public class ValidCertificateRequirement : IAuthorizationRequirement
{

}

然后设置需要应用的策略(Startup.cs, ConfigureServices()):

services.AddAuthorization(options => 
{
    options.AddPolicy("ValidateCertificate", policy => 
    {
        policy.Requirements.Add(new ValidCertificateRequirement());
    });
});

现在您需要创建需求处理程序:

public class ValidCertificateHandler : AuthorizationHandler<ValidCertificateRequirement>
{
    public ValidCertificateHandler()
    {
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ValidCertificateRequirement requirement)
    {
        var filterContext = (AuthorizationFilterContext)context.Resource;
        var Response = filterContext.HttpContext.Response;
        var message = Encoding.UTF8.GetBytes("Invalid certificate");
        Response.OnStarting(async () =>
        {
            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            await Response.Body.WriteAsync(message, 0, message.Length);
        });
        context.Fail();
        return Task.CompletedTask;
    }
}

然后需要在启动时注册处理程序(在 ConfigureServices() 中):

//Register handler
services.AddSingleton<IAuthorizationHandler, ValidCertificateHandler>();

最后,对于任一方法(断言或实施),将 Authorize 属性应用于您的操作,指明要应用的策略:

[Authorize(Policy = "ValidateCertificate")]
public HttpResponseMessage TestAuth()
{
    return new HttpResponseMessage(HttpStatusCode.OK)
    {
        ReasonPhrase = "In Test method without any authorization."
    };
}

您可以在此处阅读更多相关信息:

Policy-based authorization in ASP.NET Core