Точка останова не попадает в 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. Хотя, видимо, это только для маршрутизации, но я подумал, что это стоит упомянуть.

Я установил точку останова в первой строке кода атрибута, но она не сработала. Я считаю, что он должен получить удар до того, как действие будет выполнено, и что действие никогда не должно выполняться, потому что я устанавливаю ответ внутри атрибута. Почему атрибут не выполняется?


person birdus    schedule 15.05.2019    source источник
comment
Попробуйте наследовать от класса AuthorizeAttribute и убедитесь, что он находится в пространстве имен System.Web.Http.   -  person JuanR    schedule 15.05.2019
comment
@JuanR Казалось, это не имеет никакого значения.   -  person birdus    schedule 15.05.2019
comment
Вы уверены, что ваше действие вообще выполняется?   -  person JuanR    schedule 15.05.2019
comment
Да. Выполнение останавливается в точке останова в действии. Хороший вопрос!   -  person birdus    schedule 15.05.2019


Ответы (1)


Этого можно добиться с помощью авторизации на основе политик.

По сути, идея состоит в том, что у вас есть требование, которое необходимо выполнить (действительный сертификат), обработчик требования (как проверить сертификат) и политика, которая обеспечивает соблюдение этого требования и применяется во время авторизации.

Если ваш код достаточно прост, вы можете просто указать 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."
    };
}

Вы можете прочитать больше об этом здесь:

Авторизация на основе политик в ASP.NET Ядро

person JuanR    schedule 15.05.2019
comment
К сожалению, это не имело никакого значения. - person birdus; 16.05.2019
comment
Кстати, обычная маршрутизация даже не настроена, поэтому маршрут атрибута, который у меня есть для рассматриваемого действия, должен быть тем, который используется. Итак, это удовлетворяет документации, которую вы упомянули (очевидно). - person birdus; 16.05.2019
comment
@birdus: вы устанавливаете совместимость службы Mvc? services.AddMvc() .SetCompatibilityVersion(CompatibilityVersion.VersionApiController2); Это необходимо при использовании атрибута ApiController. - person JuanR; 16.05.2019
comment
Да. Он был там при создании проекта. - person birdus; 16.05.2019
comment
@birdus: Вот идея. Предоставьте конструктор для атрибута и поместите там точку останова. Посмотри, загорится ли. По крайней мере, мы будем знать, что атрибут создается (что, я думаю, так и есть). Кроме того, вы можете использовать это, чтобы установить ошибку: context.Response = context.ControllerContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Your error message"); - person JuanR; 16.05.2019
comment
@birdus: я обновил свой ответ. Это должно сделать это. Я испытал это на себе! - person JuanR; 16.05.2019
comment
Думаю, ближе. Теперь я получаю: InvalidOperationException: не была указана схема аутентификации, и не было найдено DefaultChallengeScheme. Microsoft.AspNetCore.Authentication.AuthenticationService.ChallengeAsync (контекст HttpContext, строковая схема, свойства AuthenticationProperties) - person birdus; 16.05.2019
comment
Да, но если вы посмотрите на необработанный ответ, вы увидите, что возвращенный код является правильным. Вы спросили об авторизации, и это отвечает на эту часть. Поскольку то, что вы проверяете, является сертификатом, я полагаю, что вместо этого вы можете реализовать это в схеме аутентификации. Проверьте это: stackoverflow.com/questions/47324129/ не было - person JuanR; 16.05.2019
comment
Спасибо за все твои усилия, Хуан. Я отмечу ваш ответ как правильный и посмотрю, смогу ли я понять, почему я получаю другую ошибку. - person birdus; 21.05.2019
comment
@birdus: я рад, что смог помочь. Пройдите по ссылке, которую я разместил. Это должно сработать. Удачи! - person JuanR; 21.05.2019