Авторизовать только определенные методы Http в ASP.NET Core

Я хотел бы потребовать одну политику для всех действий на контроллере, и я хотел бы также потребовать вторую политику для всех вызовов «методов редактирования» HTTP (POST, PUT, PATCH и DELETE). То есть методы редактирования должны требовать обеих политик. Из-за требований к реализации, а также из-за желания сохранить код DRY, мне нужно, чтобы последняя политика применялась на уровне контроллера, а не дублировалась во всех методах действий.

В качестве простого примера у меня есть PeopleController, и у меня также есть два разрешения, реализованные как политики, ViewPeople и EditPeople. Прямо сейчас у меня есть:

[Authorize("ViewPeople")]
public class PeopleController : Controller { }

Как мне добавить политику / разрешение EditPeople, чтобы они «складывались» и применялись только к командам редактирования?

Я столкнулся с двумя проблемами, которые кажутся мне настоящей болью:

  • Вы не можете иметь более одного AuthorizeAttribute или более одной политики, указанной в AuthorizeAttribute, AFAIK.
  • Вы не можете получить доступ к запросу в настраиваемом AuthorizationHandler, поэтому я не могу проверить HttpMethod, чтобы проверить его.

Я попытался обойти первое с помощью настраиваемого Requirement и AuthorizationHandler, например:

public class ViewEditRolesRequirement : IAuthorizationRequirement
{
    public ViewEditRolesRequirement(Roles[] editRoles, Roles[] viewRoles)
        => (EditRoles, ViewRoles) = (editRoles, viewRoles);

    public Roles[] EditRoles { get; }
    public Roles[] ViewRoles { get; }
}

public class ViewEditRolesHandler : AuthorizationHandler<ViewEditRolesRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ViewEditRolesRequirement requirement)
    {
        if (context.User != null)
        {
            var canView = requirement.ViewRoles.Any(r => context.User.IsInRole(r.ToString()));
            var canEdit = requirement.EditRoles.Any(r => context.User.IsInRole(r.ToString()));
            if (context. // Wait, why can't I get to the bloody HttpRequest??
        }
        return Task.CompletedTask;
    }
}

... но я дошел до if (context., прежде чем понял, что у меня нет доступа к объекту запроса.

Единственный ли мой выбор - переопределить метод OnActionExecuting в контроллере и выполнить там свою авторизацию? Я полагаю, это по крайней мере осуждается?


person pbarranis    schedule 21.08.2018    source источник


Ответы (2)


Вы не можете получить доступ к запросу в настраиваемом AuthorizationHandler, поэтому я не могу проверить HttpMethod ...

На самом деле мы может получить доступ к запросу в AuthorizationHandler. Мы делаем это, преобразовывая context.Resource с ключевым словом as. Вот пример:

services.AddAuthorization(config =>
{
    config.AddPolicy("View", p => p.RequireAssertion(context =>
    {
        var filterContext = context.Resource as AuthorizationFilterContext;
        var httpMethod = filterContext.HttpContext.Request.Method;
        // add conditional authorization here
        return true; 
    }));

    config.AddPolicy("Edit", p => p.RequireAssertion(context =>
    {
        var filterContext = context.Resource as AuthorizationFilterContext;
        var httpMethod = filterContext.HttpContext.Request.Method;
        // add conditional authorization here
        return true;
    }));
});

У вас не может быть более одного AuthorizeAttribute ....

На самом деле у нас может быть более одного AuthorizeAttribute. Примечание из указывает, что атрибут имеет AllowMultiple=true. Это позволяет нам «складывать» их. Вот пример:

[Authorize(Policy="View")]
[Authorize(Policy="Edit")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    ...
}
person Shaun Luttin    schedule 21.08.2018
comment
Превосходно. Что касается проблемы с несколькими, я уверен, что в сообщении об ошибке, которое я видел, говорится, что AllowMultiple = false; Я не стал проверять исходный код на GitHub до сих пор, так как сообщение было настолько четким. Мне нужно выяснить, почему я увидел то, что видел. Спасибо. - person pbarranis; 23.08.2018
comment
Итак, с учетом сказанного, я понимаю, что это быстрый путь к решению, но ищу совета здесь ... это лучший способ выполнить то, что я пытаюсь сделать? Я чувствую, что то, что я делаю, действительно запутано. То же самое в MVC 6 было всего 10 строк кода в настраиваемом атрибуте. Совет / мысли? - person pbarranis; 23.08.2018
comment
Я не знаю, как лучше добиться того, что вы пытаетесь сделать. Все, что я могу предложить, - это внимательно прочитать документацию по авторизации здесь: docs.microsoft.com/en-us/aspnet/core/security/authorization/ - person Shaun Luttin; 23.08.2018
comment
@ShaunLuttin знаете ли вы, применимы ли эти политики к конкретным методам обработчика? Я пытался сделать то же, что вы здесь показали, но безуспешно. Документы говорят об ограничении метода, используйте фильтр, но я считаю, что не рекомендуется кодировать фильтр для каждой страницы, а также монолитную модель BasePageModel для всех фильтров. - person B. León; 22.01.2019
comment
@ B.León Я не знаю, что я думаю. - person Shaun Luttin; 22.01.2019
comment
Не волнуйтесь, я нашел обходной путь, назначив утверждения ролям, получая их в User.Claims (он запрашивает aspnetroleclaims, поскольку RoleClaims наследуются его пользователями), к какому методу можно получить доступ через httpcontext, который также доступен в политиках, определенных в Startup. Много беготни, но работает! В любом случае спасибо за быстрый ответ - person B. León; 23.01.2019