MVC Core Authorization

Я использую MVC Core для веб-сайта, где у меня есть следующая проблема, чтобы разобраться.

Допустим, у меня есть две группы GroupA и GroupB, и у этих групп есть несколько сущностей. Субъект либо принадлежит Группе A или Группе B, но не одновременно обоим. Чтобы защитить обновление этих объектов, я определил следующую политику:

AuthorizationOptions.AddPolicy("UpdateEntityInGroupAPolicy", policy =>
{
    policy.RequireClaim("ClaimThatAllowsUpdatingEntityInGroupA");
});

Эта политика гарантирует, что пользователи, которые хотят обновить сущность в GroupA, имеют претензию ClaimThatAllowsUpdatingEntityInGroupA. Аналогичная политика может быть создана для GroupB с заявкой ClaimThatAllowsUpdatingEntityInGroupB.

Более того, давайте предположим, что GroupA имеет сущность с идентификатором 5, а GroupB имеет сущность с идентификатором 10. Если данный пользователь (у которого есть утверждение ClaimThatAllowsUpdatingEntityInGroupA, но не утверждение ClaimThatAllowsUpdatingEntityInGroupB) обновляет сущность с идентификатором 5 по адресу url:

http://example.com/entity/5/update

Как мне теперь убедиться, что пользователь не просто меняет 5 в URL-адресе на 10, а фактически обновляет другой объект с идентификатором 10 в GroupB?

Я надеюсь, что я ясен с вопросом и спасибо за любую помощь:)

1 ответ

Решение

Если вы хотите использовать Authorize атрибут, вы не можете сделать это, потому что фильтр авторизации не знает о вашей сущности. Таким образом, для вашего случая авторизация на основе ресурсов кажется правильным решением.

Сначала предположим, что ваша сущность похожа на:

public class YourEntity
{
    public int Id { get; set; }
    public string Group { get; set; }
    //...
}

Для реализации авторизации на основе ресурсов

Создайте требование:

public class UpdateRequirement : IAuthorizationRequirement
{
}

Создать обработчик авторизации для требования и объекта

public class YourEntityAuthorizationHandler : AuthorizationHandler<UpdateRequirement, YourEntity>
   {
       public override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   UpdateRequirement requirement,
                                                   YourEntity resource)
       {
           if(resource.Group == "GroupA" &&
              context.User.HasClaim("ClaimThatAllowsUpdatingEntityInGroupA"))
           {
               context.Succeed(requirement);
           }
           else if(resource.Group == "GroupB" &&
              context.User.HasClaim("ClaimThatAllowsUpdatingEntityInGroupB"))
           {
               context.Succeed(requirement);
           }
           return Task.CompletedTask;
       }
   }

Наконец, используйте его в логике вашего контроллера:

    public async Task<IActionResult> Update([FromServices]IAuthorizationService authorizationService, int id)
    {
        var entity= _entityRepository.Get(id);

        if (entity == null)
        {
            return new NotFoundResult();
        }

        if (await _authorizationService.AuthorizeAsync(User, entity, new UpdateRequirement()))
        {
            return View(entity);
        }
        else
        {
            return new ChallengeResult();
        }
    }

Также смотрите пример @blowdart

Другие вопросы по тегам