Поддержка нескольких фильтров авторизации для одного действия в ASP.NET Core

У нас есть IAuthorizationFilter, и мы можем использовать его для авторизации запросов.

Но есть одна проблема. Я должен поддерживать несколько фильтров авторизации, например:

  1. BearerTokenAuthorizationFilter
  2. ApiKeyAuthorizationFilter

Я бы получил такое поведение:

Во-первых, BearerTokenAuthorizationFilter проверяет, начинается ли заголовок авторизации с Bearer. Если нет, то если для текущего действия применим другой фильтр авторизации, то запускаем этот другой фильтр авторизации. Если нет, верните ошибку «HTTP 401 Unauthorized».

И то же самое с ApiKeyAuthorizationFilter. В начале он проверяет, начинается ли заголовок авторизации с Basic. Если нет, проверяет, существует ли другой фильтр авторизации, и если да, то запускает его. В противном случае вернуть ошибку «HTTP 401 Unauthorized».

И чтобы предотвратить бесконечный цикл, нам нужно как-то хранить информацию о том, что конкретный фильтр авторизации уже был выполнен.

Это легко сделать в AuthenticationHandler. Мы можем вернуть AuthenticateResult.NoResult(), когда конкретный AuthenticationHandler неприменим для обработки этого запроса, и будет запущен другой. Если все было выполнено, то вернуть 401 Unauthorized.

Так что у меня был бы такой же механизм, как и для AuthenticationHandler (AuthenticateResult.NoResult()). Как я могу добиться этого, используя AuthorizationFilter?

У вас может быть несколько атрибутов авторизации в вашем действии или контроллере.

thanzeel 11.02.2023 13:25

Я могу, но это не работает. Если один из фильтров авторизации дает сбой, то другие не запускаются. Поэтому я не могу поддерживать множество фильтров авторизации для одного действия (не могу поддерживать разные методы авторизации для одного действия).

Szyszka947 11.02.2023 14:08
Стоит ли изучать 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
2
85
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Несколько экземпляров AuthorizeAttribute обрабатываются MVC, как если бы они были объединены с помощью AND. Если вам нужно поведение ИЛИ, вам нужно будет реализовать собственную логику для проверок. Желательно реализовать AuthAttribute

Есть два способа

  1. Создайте собственный фильтр аутентификации для реализации вашей логики (рекомендуется)
  2. Сделайте так, чтобы ваша основная конечная точка получала запрос, извлекала тип и перенаправляла его на соответствующее действие для типа аутентификации. Итак, теперь вы можете заглянуть в заголовки и перейти к правильному фильтру авторизации.

Любые примеры первого подхода? Я не понимаю, как я могу добиться поведения ИЛИ с помощью пользовательского фильтра аутентификации, если я хочу, чтобы многие фильтры авторизации в одном действии имели поведение ИЛИ.

Szyszka947 11.02.2023 15:50
c-sharpcorner.com/article/…
Sundar Babu Manoharan 11.02.2023 16:08

В статье, на которую вы ссылались, используется ASP NET MVC. Это не работает в ASP NET Core с NET 7.

Szyszka947 11.02.2023 16:22

Это просто идея, стоящая за этим. Вот основная версия google.com/amp/s/www.infoworld.com/article/3622962/…

Sundar Babu Manoharan 11.02.2023 21:25

Я думаю, вы можете сделать Policy based Авторизацию. Поэтому для этого вам нужно написать пользовательскую политику. вы можете проверить несколько случаев в рамках одной политики

это объясняется здесь

https://learn.microsoft.com/en-us/aspnet/core/security/authorization/iauthorizationpolicyprovider?view=aspnetcore-7.0

Я сделал то же самое, используя пользовательские политики, как показано ниже.

public sealed class CompanyAccessRequirementHandler : AuthorizationHandler<CompanyAccessRequirement>
{
    private readonly ILoggedInUserService _loggedInUserService;
    private readonly IUserSecurityRespository _userSecurityRespository;

    public CompanyAccessRequirementHandler(ILoggedInUserService loggedInUserService, IUserSecurityRespository userSecurityRespository)
    {
        _loggedInUserService = loggedInUserService;
        _userSecurityRespository = userSecurityRespository;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, CompanyAccessRequirement requirement)
    {
        if (context.User.Identity!.IsAuthenticated is false)
        {
            context.Fail();
            return;
        }

        var userEmail = _loggedInUserService.UserEmail;

        if (userEmail is null)
        {
            context.Fail();
            return;
        }

        var user = await _userSecurityRespository.GetUser(context.User);

        if (user is null)
        {
            context.Fail();
            return;
        }


        var isSuperAdmin = _loggedInUserService.UserId == AppConstants.SuperAdmin.SuperUserId.ToString();

        if (isSuperAdmin)
        {
            context.Succeed(requirement);
            return;
        }

        var hasAccessToCompany = await _userSecurityRespository.HasAccessToCompanies(user.Id, _loggedInUserService.CompanyIds);

        if (hasAccessToCompany)
        {
            context.Succeed(requirement);
            return;
        }

        else
        {
            context.Fail();
        }
    }

Что делать, если я указал несколько политик в атрибуте [Authorize]? Являются ли они ANDed или ORed ? не могу найти информацию об этом. И если они объединились, как я могу добиться поведения ИЛИ?

Szyszka947 11.02.2023 16:58

Его политика оценивается на And условиях

thanzeel 11.02.2023 17:34

Политики не работают, потому что у меня нет схемы аутентификации. Мой API интересует не «кто запросил», а «действителен ли данный ключ API».

Szyszka947 11.02.2023 23:11

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

thanzeel 12.02.2023 02:05

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

Szyszka947 12.02.2023 09:38
Ответ принят как подходящий

Я думаю, для вашего требования должно быть три фильтра

Scheme Filter -Проверьте правильность схемы авторизации, если она неверна, верните ошибку

BearerTokenAuthorizationFilter -если схема "На предъявителя",если да то проверить претензии из строки ,вернуть не получится если претензии неверны

ApiKeyAuthorizationFilter -если схема «Базовая», если да, то проверьте, что пользователь/пароль из строки возвращает ошибку, если пользователь/пароль не верны

Я пробовал, как показано ниже:

public class SchemeFilter : IAuthorizationFilter
{
    private readonly string[] Schemes = new string[] { "Bearer", "Basic"};

    private bool exist { get; set; }=false;

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        if (context.HttpContext.Request.Headers.ContainsKey("Authorization"))
        {

       
        var str = context.HttpContext.Request.Headers["Authorization"].ToString();
        foreach (var scheme in Schemes)
        {
            exist=str.StartsWith(scheme);
           
            if (exist)
            {
               
                break;
            }
           
        }


        }
        if (!exist)
        {
            context.Result = new UnauthorizedResult();
        }


    }
}

public class BasicFilter : IAuthorizationFilter
{
    private readonly string Scheme = "Basic";

   

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var check=context.HttpContext.Request.Headers.TryGetValue("Authorization", out var value);
         
        if ( value[0].StartsWith(Scheme))
        {
            var basicstr = value[0];
            //modify the logical here yourself
            if (basicstr != "Basic expectedbasicstr")
            {
                context.Result = new UnauthorizedResult();
            }
        }
    }
}


public class BearerFilter : IAuthorizationFilter
{

    private readonly string Scheme = "Bearer";
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var check = context.HttpContext.Request.Headers.TryGetValue("Authorization", out var value);
         
        if ( value[0].StartsWith(Scheme))
        {
            var bearerstr = value[0];
            //modify the logical here yourself
            if (bearerstr != "Bearer expectedbearerstr")
            {
                context.Result = new UnauthorizedResult();
            }

        }
    }
}

Результат:

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