Разрешение на сравнение претензии с текущим запросом - PullRequest
1 голос
/ 17 июня 2020

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

У меня есть Razor Pages, и я хочу добавить некоторые логики авторизации c, чтобы гарантировать, что запрос

https://localhost:5001/admin/subscriptions/123`

, где 123 - это любой Guid, это {subscriptionId}.

Я использую аутентификацию на основе Cook ie, поэтому у меня должно быть требование Name или подобное с Speci c subscriptionId, например 124 для текущего запроса.

Я хочу обработать эту подписку, чтобы она запрещала доступ, когда subscriptionId в URL-адресе не соответствует указанному c претензии.

Это неловко, но я не знаю, как это сделать со страницами As pNet Core 3.1 и Razor.

Это просто не похоже на дизайн для такого рода Сценар ios. Примеры, которые я нахожу, предполагают, что мы заранее знаем, какими должны быть претензии. Например, в официальной документации Microsoft показано, как утверждение «возраста» можно сравнить с минимальным возрастом жестко запрограммированных magi c int 21.

Что, если оба значения зависят от запроса?

Когда я добавляю

services
                .AddAuthorization(
                    options =>
                    {
                        options.AddPolicy(
                            "SameSubscriptionIdPolicy",
                            policy =>
                                policy.RequireAssertion(context =>
                                {
                                    var claims = context.User.Claims;
                                    var subscriptionId = context.User.FindFirst(x => x.Type == ClaimTypes.Name);
                                    // how can I access here the Request and do the comparison?
                                }));
                    });

Если я получаю доступ к контексту, я вижу параметризованный URL-адрес, а не реальное значение запроса. Я вижу admin/subscriptions/{subscriptionId} вместо указанного c значения, например admin/subscriptions/124

Как мне получить доступ к URL-запросу? , чтобы я мог сравнить его со значением претензии?


ОБНОВЛЕНИЕ 1:

Я где-то читал здесь о кастинге вроде этого:

context.Resource is AuthorizationFilterContext mvcContext

Это не так работай. Ресурс AuthorizationHandlerContext

Также я пробовал следующее

services
    .AddAuthorization(
        options =>
        {
            options.AddPolicy(
                "SameSubscriptionIdPolicy",
                policy =>
                    policy.RequireAssertion(context =>
                    {
                        var claims = context.User.Claims;
                        var subscriptionId = context.User.FindFirst(x => x.Type == ClaimTypes.Name);
                        var accessor = new HttpContextAccessor();
                        var url = accessor.HttpContext.GetRouteData();
                        return false;
                    }));
        });

, но HttpContext имеет значение null . Может, к моменту запуска политики запроса еще нет?

1 Ответ

2 голосов
/ 17 июня 2020

Это заняло некоторое время, но мне удалось заставить его работать для DotNetCore 3.1 +.

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

public class SubscriptionRequirement
    : IAuthorizationRequirement
{
    public string ClaimName { get; }

    public SubscriptionRequirement(string claimName)
    {
        ClaimName = claimName;
    }
}

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

затем AuthorizationHandler, способный получить доступ к утверждениям (от cook ie или что-то еще) и http-запрос.

public class SubscriptionRequirementHandler
    : AuthorizationHandler<SubscriptionRequirement>
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public SubscriptionRequirementHandler(
        IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        SubscriptionRequirement subscriptionRequirement)
    {
        var claims = context.User.Claims;
        var subscriptionId = 
            context
                .User
                .FindFirst(x => x.Type == subscriptionRequirement.ClaimName)
                .Value;

        var httpContext = _httpContextAccessor.HttpContext;
        var subscriptionIdInUrl = httpContext.Request.RouteValues["subscriptionId"];
        if (subscriptionIdInUrl != null)
        {
            var isAuthorized = subscriptionId == subscriptionIdInUrl.ToString();
            if (isAuthorized)
            {
                context.Succeed(subscriptionRequirement);
                return Task.CompletedTask;
            }
        }

        context.Fail();
        return Task.CompletedTask;
    }
}

Чтобы добавить эту политику:

services
    .AddAuthorization(
        options =>
        {
            options.AddPolicy(
                "SameSubscriptionIdPolicy",
                policy =>
                    policy.Requirements.Add(new SubscriptionRequirement(ClaimTypes.Name)));
        });

Я также могу (необязательно для Razor Pages) укажите, что я хочу применить эту политику ко всем страницам в указанной c папке

services
    .AddRazorPages(
        options =>
        {
            options.Conventions.AuthorizeFolder("/admin", "SameSubscriptionIdPolicy");
            options.Conventions.Add(new PageRouteTransformerConvention(new LowerCaseRoutes()));
        });

и, наконец, что очень важно, зарегистрируйте обработчик и http

services.AddSingleton<IAuthorizationHandler, SubscriptionRequirementHandler>();
services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();

ОБНОВЛЕНИЕ 2020/06/18: Согласно комментарию Кирка, более приятный способ зарегистрировать HttpContextAccessor - это сделать это как синглтон, используя вместо этого уже доступное расширение.

services.AddHttpContextAccessor();
...