ASP. NET Core AuthorizationHandler не вызывается - PullRequest
0 голосов
/ 18 апреля 2020

Я пытаюсь добавить пользовательскую роль на основе authorisation, но мне не удается настроить автозапуск для вызова моего AuthorizationHandler.

Я нашел некоторую связанную информацию на GitHub: здесь . Это ошибка или нет?

Я использую ASP.NET Core 3.1, и моя инициализация выглядит следующим образом:

1: Это извлекает URL / роли из базы данных, используя Dapper ORM:

private List<UrlRole> GetRolesRoutes()
{
    var urlRole = DapperORM.ReturnList<UrlRole>("user_url_role_all");
    return urlRole.Result.ToList();
}

2: При запуске я получаю URL / роли и сохраняю результат в глобальной переменной:

public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
    this.environment = env;
    UrlRoles = GetRolesRoutes();
}

3: Моя конфигурация: Примечание UrlRoles, который передается по

 public void ConfigureServices(IServiceCollection services)
 {
     // .. snip   
     services.AddAuthorization(o =>
     o.AddPolicy(_RequireAuthenticatedUserPolicy,
            builder => builder.RequireAuthenticatedUser()));

     services.AddAuthorization(options =>
     {
         options.AddPolicy("Roles", policy =>
         policy.Requirements.Add(new UrlRolesRequirement(UrlRoles)));
     });


    services.AddSingleton<AuthorizationHandler<UrlRolesRequirement>, PermissionHandler>();
}

5: Мой обработчик: который не вызывается

public class PermissionHandler : AuthorizationHandler<UrlRolesRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UrlRolesRequirement urlRolesRequirement)
    {
        var pendingRequirements = context.PendingRequirements.ToList();
        foreach (var requirement in pendingRequirements)
        {
        }
        return Task.CompletedTask;
    }
}

6: Мой класс требований:

public class UrlRolesRequirement : IAuthorizationRequirement
{
    private List<UrlRole> UrlRoles { get; }

    public UrlRolesRequirement(List<UrlRole> urlRoles)
    {
        UrlRoles = urlRoles;
    }      
}

Когда я отлаживаю ASP.NET Core AuthorizationHandler, я никогда не вижу, что мой пользовательский Требование как требование, которое я настроил при запуске. Я ожидал увидеть требование, и если требование присутствует, то, я предполагаю, произойдет «обратный вызов». Но по какой-то причине моя конфигурация не может добавить требование.

public virtual async Task HandleAsync(AuthorizationHandlerContext context)
{
    if (context.Resource is TResource)
    {
        foreach (var req in context.Requirements.OfType<TRequirement>())
        {
            await HandleRequirementAsync(context, req, (TResource)context.Resource);
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 30 апреля 2020

Вот проверенное решение, которое позволяет runtime configuration changes. Также освобождая от необходимости декорировать каждый класс или действие.

В Startup Добавьте Role Authorization Requirement, а также зарегистрируйте RoleService, который будет отвечать за обеспечение доступа определенной роли к определенному URL.

Вот файл Startup.cs, в котором мы настраиваем требование, а также службу роли:

services.AddAuthorization(options =>
{
    options.DefaultPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddRequirements(new UrlRolesRequirement())
        .Build();
});

services.AddSingleton<IUserService, UserService>(); // authenticate
services.AddSingleton<IUserRoleService, UserRoleService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IAuthorizationHandler, PermissionHandler>(); // authorise

Роль IUserRoleService: - реализация UserRoleService проверяет заявленную роль пользователя (утверждение JWT) против карты конфигурации, состоящей из разрешенных записей URL / ролей, путем поиска в кэшированной карте или извлечения данных из базы данных.

Типичные url(path) до role Карта имеет следующий формат, извлекается из базы данных, затем кэшируется (, если при поиске происходит сбой, данные извлекаются из базы данных ):

/path/to/resource        ROLE
public interface IUserRoleService
{
    public bool UserHasAccess(ClaimsPrincipal user, string path);
}

Обработчик разрешений:

public class PermissionHandler : IAuthorizationHandler
{
    private readonly IUserRoleService userRoleService;
    private readonly IHttpContextAccessor contextAccessor;

    public PermissionHandler(IUserRoleService userRoleService, IHttpContextAccessor contextAccessor)
    {
        this.userRoleService = userRoleService;
        this.contextAccessor = contextAccessor;
    }

    public Task HandleAsync(AuthorizationHandlerContext context)
    {
        var pendingRequirements = context.PendingRequirements.ToList();
        foreach (var requirement in pendingRequirements)
        {
            if (!(requirement is UrlRolesRequirement)) continue;

            var httpContext = contextAccessor.HttpContext;
            var path = httpContext.Request.Path;
            if (userRoleService.UserHasAccess(context.User, path))
            {
                context.Succeed(requirement);
                break;
            }
        }
        return Task.CompletedTask;
    }
}

Требование Roles - просто POCO

public class UrlRolesRequirement : IAuthorizationRequirement
{           
}

Вот частичная реализация UserRoleService которая проверяет заявленную роль JWT.

private bool ValidateUser(ClaimsPrincipal user, string path)
{
    foreach (var userClaim in user.Claims)
    {
        if (!userClaim.Type.Contains("claims/role")) continue;

        var role = userClaim.Value;
        var key = role + SEPARATOR + path;

        if (urlRoles.ContainsKey(key))
        {
            var entry = urlRoles[key];
            if (entry.Url.Equals(path) && entry.Role.Equals(role))
            {
                return true;
            }
        }

    }
    Console.WriteLine("Access denied: " + path);
    return false;
}
0 голосов
/ 18 апреля 2020

Без указания ASP. NET Ядро для этого не будет использовать настроенную политику для авторизации чего-либо. Политики авторизации позволяют заранее задавать сложные условия авторизации, чтобы вы могли использовать это поведение в случае необходимости. Однако он не применяется по умолчанию и не может учитывать, что вы уже настроили две политики: какие из них должны применяться? Все они? Тогда зачем настраивать отдельные политики?

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

[Authorize("Roles")] // ← this is the policy name
public class ExampleController : Controller
{
    // …
}

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

services.AddAuthorization(options =>
{
    options.DefaultPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
}

Например, это определит политику, которая требует аутентифицированных пользователей по умолчанию. Таким образом, всякий раз, когда вы используете атрибут [Authorize] без указания явной политики, он будет использовать эту политику по умолчанию.

Все это все равно потребует от вас пометить Маршруты как-то, что вам требуется авторизация. Помимо использования атрибута [Authorize], вы также можете сделать это в более центральном месте: вызов app.UseEndpoints() в вашем классе Startup.

endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}")
    .RequireAuthorization("Roles");

Это шаблон маршрута по умолчанию для контроллеров, но с вызов RequireAuthorization, для которого в основном потребуется политика авторизации Roles на всех маршрутах, соответствующих этому шаблону маршрута.

Вы также можете использовать это место для настройки различных политик авторизации по умолчанию для различных маршрутов: Разделив ваш шаблон маршрута, вы можете сделать несколько вызовов на MapControllerRoute с различными шаблонами маршрутов, которые все задают свою собственную политику авторизации.

Я думал, что вместо того, чтобы украшать каждый контроллер или действие, Я скорее хотел иметь некоторую карту предварительной конфигурации в БД, а затем в конвейере проверять роль или роли пользователей, которые выделяются при аутентификации пользователя. Когда пользователь затем пытается получить доступ к URL-адресу, роль пользователя проверяется, и доступ предоставляется или отклоняется.

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...