Множественная аутентификация .NET Core с авторизацией поставщика динамической политики

API .NET Core 2.0:

Сначала я попытался использовать IAuthorizationPolicyProvider для динамических политик:

public class MyAuthorizationPolicyProvider: IAuthorizationPolicyProvider
{
    internal const string PolicyPrefix = "MyJwt";

    public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
    {
        return Task.FromResult<AuthorizationPolicy>(new AuthorizationPolicyBuilder(Array.Empty<string>()).RequireAuthenticatedUser().Build());
    }

    public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        if (!policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
            return Task.FromResult<AuthorizationPolicy>((AuthorizationPolicy)null);

        var authorizationPolicyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
        authorizationPolicyBuilder.AddRequirements((IAuthorizationRequirement)new MyAuthorizationRequirement(policyName.Substring(PolicyPrefix.Length)));
        return Task.FromResult<AuthorizationPolicy>(authorizationPolicyBuilder.Build());
    }
}



public class MyAuthorizationHandler: AuthorizationHandler<MyAuthorizationRequirement>
{

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyAuthorizationRequirement requirement)
    {
        if (!context.User.Identity.IsAuthenticated)
        {
            context.Fail();
            return Task.CompletedTask;
        }



        if (SomeLibrary.IsContextValid(context, requirement))
            context.Succeed((IAuthorizationRequirement)requirement);
        else
            context.Fail();

        return Task.CompletedTask;
    }
}

И добавьте аутентификацию:

services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

    }).AddJwtBearer(options =>
    {
        options.TokenValidationParameters = GetTokenValidationParameters();
    });

    services.AddSingleton<IAuthorizationHandler, MyAuthorizationHandler>();
    services.AddSingleton<IAuthorizationPolicyProvider, MyAuthorizationPolicyProvider>();

И мой атрибут авторизации:

public class MyAuthorizeAttribute: AuthorizeAttribute
{

    internal const string PolicyPrefix = "MyJwt";

    public MyAuthorizeAttribute(string permission)
    {
        this.Permission = permission;
    }

    public string Permission
    {
        get
        {
            if (this.Policy.Length < PolicyPrefix.Length)
                return (string)null;
            return this.Policy.Substring(PolicyPrefix.Length);
        }
        set
        {
            this.Policy = PolicyPrefix + value;
        }
    }
}

И я использовал: [MyAuthorize("anythings")]

Пока все было хорошо. Теперь мне нужно добавить новую аутентификацию (я имею в виду, что обе работают вместе).

Поэтому я изменил MyAuthorizationPolicyProvider для поддержки новых политик:

public class MyAuthorizationPolicyProvider: IAuthorizationPolicyProvider
    {
        internal const string PolicyPrefix = "MyJwt";

        public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
        {
            return Task.FromResult<AuthorizationPolicy>(new AuthorizationPolicyBuilder(Array.Empty<string>()).RequireAuthenticatedUser().Build());
        }

        public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
        {

             if (policyName == "newpolicy") //here
                return AddNewPolicy();


            if (!policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
                return Task.FromResult<AuthorizationPolicy>((AuthorizationPolicy)null);

            var authorizationPolicyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
            authorizationPolicyBuilder.AddRequirements((IAuthorizationRequirement)new MyAuthorizationRequirement(policyName.Substring(PolicyPrefix.Length)));
            return Task.FromResult<AuthorizationPolicy>(authorizationPolicyBuilder.Build());
        }
    }

И Изменить Добавить Аутентификацию:

services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

    }).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        options.TokenValidationParameters = GetTokenValidationParameters();
    })
    .AddJwtBearer("newSchema", options =>
    {
        options.TokenValidationParameters = GetNewSchemaTokenValidationParameters();
    });

    services.AddSingleton<IAuthorizationHandler, MyAuthorizationHandler>();
    services.AddSingleton<IAuthorizationHandler, MyNewAuthorizationHandler>();
    services.AddSingleton<IAuthorizationPolicyProvider, MyAuthorizationPolicyProvider>();

А MyNewAuthorizationHandler это:

public class MyNewAuthorizationHandler: AuthorizationHandler<MyNewAuthorizationRequirement>
{

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyNewAuthorizationRequirement requirement)
    {
        if (!context.User.Identity.IsAuthenticated)
        {
            context.Fail();
            return Task.CompletedTask;
        }



        if (SomeNewLibrary.IsContextValid(context, requirement))
            context.Succeed((IAuthorizationRequirement)requirement);
        else
            context.Fail();

        return Task.CompletedTask;
    }
}

И я использую [Authorize(Policy = "newpolicy")]

Теперь первая аутентификация работает нормально с MyAuthorizationHandler, но вторая аутентификация не удалась в MyNewAuthorizationHandler, потому что пользователь не аутентифицирован, и эта строка не вернула: if (!context.User.Identity.IsAuthenticated)

Что я пропустил?

Как я могу иметь мультиаутентификацию и в то же время использовать IAuthorizationPolicyProvider для динамического добавления политик?

Я знаю, если я изменю AddAuthentication на:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = "newSchema"; //here
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.TokenValidationParameters = GetTokenValidationParameters();
})
.AddJwtBearer("newSchema", options =>
{
    options.TokenValidationParameters = GetNewSchemaTokenValidationParameters();
});

Тогда второй работает нормально, а первый не работает. Как я могу аутентифицировать обе схемы?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
602
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

После некоторого поиска вот что я нашел: поставщики политики множественной авторизации.

Первое изменение MyAuthorizationPolicyProvider для поддержки всех остальных политик:

public class MyAuthorizationPolicyProvider: IAuthorizationPolicyProvider
{
    private readonly DefaultAuthorizationPolicyProvider _fallbackPolicyProvider;
    internal const string PolicyPrefix = "MyJwt";

    public MyAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options)
    {
        _fallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
    }


    public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
    {
        return Task.FromResult<AuthorizationPolicy>(new AuthorizationPolicyBuilder(Array.Empty<string>()).RequireAuthenticatedUser().Build());
    }

    public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {

        if (!policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
            return _fallbackPolicyProvider.GetPolicyAsync(policyName); //here return back to static policies

        var authorizationPolicyBuilder = new AuthorizationPolicyBuilder(Array.Empty<string>());
        authorizationPolicyBuilder.AddRequirements((IAuthorizationRequirement)new MyAuthorizationRequirement(policyName.Substring(PolicyPrefix.Length)));
        return Task.FromResult<AuthorizationPolicy>(authorizationPolicyBuilder.Build());
    }
}

Затем измените способ добавления аутентификаций:

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.TokenValidationParameters = GetTokenValidationParameters();
})
.AddJwtBearer("newSchema", options =>
{
    options.TokenValidationParameters = GetNewSchemaTokenValidationParameters();
});



services.AddAuthorization(options =>
{
    options.DefaultPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddAuthenticationSchemes("newSchema") //add new schemas here
        .Build();

    //here: add static policies as much as you want   
    options.AddPolicy("newpolicy", new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddAuthenticationSchemes("newSchema")
        .AddRequirements(new MyNewAuthorizationRequirement("newpolicy"))
        .Build());

});

services.AddSingleton<IAuthorizationHandler, MyAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, MyNewAuthorizationHandler>();
services.AddSingleton<IAuthorizationPolicyProvider, MyAuthorizationPolicyProvider>();

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

Получение несанкционированной html-страницы IIS вместо несанкционированного ответа веб-API для несанкционированного статуса
Каков правильный способ аутентификации от JavaScript в HTML-файле до элемента управления Microsoft Web Chat для Bot Framework v4?
Request.user.is_authenticated работает только на домашней странице
Как обеспечить работу запросов к моим серверам только из одного источника/пользовательского интерфейса?
Microsoft Outlook — добавить запрос на авторизацию с пользовательским параметром
Необходимо сохранить токен доступа, отправленный из бэкэнда (json) в локальное хранилище, и войти в систему, используя этот токен. Как?
Использование олицетворения Windows, но получение ошибок доступа к файлам
Node.js: вернуть данные клиенту с помощью чистого html
Уязвим, когда пользователь нажимает на вредоносную ссылку
Как использовать loopback IP для получения токена авторизации