Множественные атрибуты авторизации по методу

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

Класс Athorization выглядит следующим образом:

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class AuthAttribute : AuthorizeAttribute {
. . .

и действие:

[Auth(Roles = AuthRole.SuperAdministrator)]
[Auth(Roles = AuthRole.Administrator, Module = ModuleID.SomeModule)]
public ActionResult Index() {
    return View(GetIndexViewModel());
}

Есть ли способ решить эту проблему или мне нужно переосмыслить мой подход?

Это должно быть запущено в MVC2.

Ответы

Ответ 1

Несколько экземпляров AuthorizeAttribute обрабатываются MVC, как если бы они были соединены с AND. Если вы хотите поведение OR, вам нужно будет реализовать свою собственную логику проверок. Предпочтительно реализовать AuthAttribute для выполнения нескольких ролей и выполнить собственную проверку с помощью логики OR.

Другим решением является использование стандартного AuthorizeAttribute и реализация пользовательского IPrincipal, который будет реализовывать метод bool IsInRole(string role) для обеспечения поведения "OR".

Пример: fooobar.com/questions/351716/...

Ответ 2

Есть лучший способ сделать это в более поздних версиях asp.net, вы можете делать как OR, так и AND в ролях. Это делается с помощью соглашения, в котором перечислены несколько ролей в одном авторизованном режиме, будет выполняться OR, где добавление нескольких атрибутов авторизации будет выполнять AND.

ИЛИ пример

[Authorize(Roles = "PowerUser,ControlPanelUser")] 

И пример

[Authorize(Roles = "PowerUser")]
[Authorize(Roles = "ControlPanelUser")]

Дополнительную информацию об этом можно найти по следующей ссылке https://docs.microsoft.com/en-us/aspnet/core/security/authorization/roles

Ответ 3

Я не уверен, что другие думают об этом, но я тоже хотел поведение OR. В моем AuthorizationHandler я только что позвонил Succeed, если кто-нибудь из них прошел. Обратите внимание, что это НЕ работает со встроенным атрибутом Authorize, который не имеет параметров.

public class LoggedInHandler : AuthorizationHandler<LoggedInAuthReq>
{
    private readonly IHttpContextAccessor httpContextAccessor;
    public LoggedInHandler(IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor;
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, LoggedInAuthReq requirement)
    {
        var httpContext = httpContextAccessor.HttpContext;
        if (httpContext != null && requirement.IsLoggedIn())
        {
            context.Succeed(requirement);
            foreach (var req in context.Requirements)
            {
                context.Succeed(req);
            }
        }

        return Task.CompletedTask;
    }
}

Предоставьте свой собственный LoggedInAuthReq. При запуске введите их в сервисы с помощью

        services.AddAuthorization(o => {
            o.AddPolicy("AadLoggedIn", policy => policy.AddRequirements(new LoggedInAuthReq()));
            ... more here
        });
        services.AddSingleton<IAuthorizationHandler, LoggedInHandler>();
        ... more here

И в вашем методе контроллера

    [Authorize("FacebookLoggedIn")]
    [Authorize("MsaLoggedIn")]
    [Authorize("AadLoggedIn")]
    [HttpGet("anyuser")]
    public JsonResult AnyUser()
    {
        return new JsonResult(new { I = "did it with Any User!" })
        {
            StatusCode = (int)HttpStatusCode.OK,
        };
    }

Это, вероятно, также может быть выполнено с помощью одного атрибута и набора операторов if. Это works for me в этом сценарии. asp.net core 2.2 на момент написания статьи.