Как реализовать контроль доступа на основе разрешения с помощью ядра Asp.Net

Я пытаюсь реализовать управление доступом на основе разрешений с ядром aspnet. Для динамического управления ролями и разрешениями пользователей (create_product, delete_product и т.д.) Они хранятся в базе данных. Модель данных похожа на http://i.stack.imgur.com/CHMPE.png

До ядра aspnet (в MVC 5) я использовал пользовательский AuthorizeAttribute, как показано ниже, чтобы решить проблему:

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    private readonly string _permissionName { get; set; }
    [Inject]
    public IAccessControlService _accessControlService { get; set; }

    public CustomAuthorizeAttribute(string permissionName = "")
    {
        _permissionName = permissionName;
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);
        var user = _accessControlService.GetUser();
        if (PermissionName != "" && !user.HasPermission(_permissionName))
        {
            // set error result
            filterContext.HttpContext.Response.StatusCode = 403;
            return;
        }
        filterContext.HttpContext.Items["CUSTOM_USER"] = user;
    }
}

Тогда я использовал его в методе действия, как показано ниже:

[HttpGet]
[CustomAuthorize(PermissionEnum.PERSON_LIST)]
public ActionResult Index(PersonListQuery query){ }

Кроме того, я использовал HttpContext.Items [ "CUSTOM_USER" ] в представлениях, чтобы показать или скрыть часть html:

@if (CurrentUser.HasPermission("<Permission Name>"))
{

}

Когда я решил переключить ядро ​​aspnet, весь мой план был неудачным. Поскольку в AuthorizeAttribute не было виртуального метода OnAuthorization. Я попробовал несколько способов решить проблему. Это ниже:

  • Использование новой авторизации на основе политик (я думаю, что она не подходит для мой scenerio)

  • Использование пользовательских AuthorizeAttribute и AuthorizationFilter (я прочитал это post qaru.site/info/334516/..., но я не смог его правильно изменить)

  • Использование специального промежуточного программного обеспечения (как получить AuthorizeAttribute текущего действие?)

  • Использование ActionFilter (правильно ли это для целей безопасности?)

Я не мог решить, какой путь лучше всего подходит для моего scenerio и как его реализовать.

Первый вопрос: Неверная практика внедрения MVC5?

Второй вопрос: Есть ли у вас предложение реализовать ядро ​​aspnet?

Ответы

Ответ 1

На основе комментариев здесь приведен пример использования авторизации на основе политик:

public class PermissionRequirement : IAuthorizationRequirement
{
    public PermissionRequirement(PermissionEnum permission)
    {
         Permission = permission;
    }

    public PermissionEnum Permission { get; }
}

public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{
    private readonly IUserPermissionsRepository permissionRepository;

    public PermissionHandler(IUserPermissionsRepository permissionRepository)
    {
        if(permissionRepository == null)
            throw new ArgumentNullException(nameof(permissionRepository));

        this.permissionRepository = permissionRepository;
    }

    protected override void Handle(AuthorizationContext context, PermissionRequirement requirement)
    {
        if(context.User == null)
        {
            // no user authorizedd. Alternatively call context.Fail() to ensure a failure 
            // as another handler for this requirement may succeed
            return null;
        }

        bool hasPermission = permissionRepository.CheckPermissionForUser(context.User, requirement.Permission);
        if (hasPermission)
        {
            context.Succeed(requirement);
        }
    }
}

И зарегистрируйте его в своем классе Startup:

services.AddAuthorization(options =>
{
    UserDbContext context = ...;
    foreach(var permission in context.Permissions) 
    {
        // assuming .Permission is enum
        options.AddPolicy(permission.Permission.ToString(),
            policy => policy.Requirements.Add(new PermissionRequirement(permission.Permission)));
    }
});

// Register it as scope, because it uses Repository that probably uses dbcontext
services.AddScope<IAuthorizationHandler, PermissionHandler>();

И, наконец, в контроллере

[HttpGet]
[Authorize(Policy = PermissionEnum.PERSON_LIST.ToString())]
public ActionResult Index(PersonListQuery query)
{
    ...
}

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

Политический подход - это предпочтительный способ сделать это командой ASP.NET Core.

Из blowdart:

Мы не хотим, чтобы вы писали пользовательские атрибуты авторизации. Если вам нужно это сделать, мы сделали что-то не так. Вместо этого вы должны писать требования авторизации.

Ответ 2

Для решения, которое не требует, чтобы вы добавляли политику для каждого разрешения, см. мой ответ для другого вопроса.

Он позволяет вам украшать ваши контроллеры и действия любыми настраиваемыми атрибутами и обращаться к ним в вашем AuthorizationHandler.