Asp.net MVC4: авторизация как для контроллера, так и для действий

Если у меня есть атрибут Authorize как на контроллере, так и на действии, какой из них будет действовать? Или оба будут действовать?

Ответ 1

Вы спросили:

Если у меня есть атрибут Authorize для обоих контроллеров и действий, какой из них будет действовать? Оба?

Чтобы ответить на это просто: оба. Эффект заключается в AND двух ограничениях вместе. Я объясню, почему ниже...

Подробнее

Итак, есть несколько причин, по которым вы могли бы спросить об этом.

  • Вы хотите знать, как применять дополнительное ограничение для Action по сравнению с методом. например
    • На уровне контроллера принудительно применяйте пользователей в роли "пользователь"
    • На уровне действия дополнительно активируйте пользователей в роли "admin"
  • Вы хотите заменить ограничение контроллера на уровне действия
  • Вы хотите удалить ограничение контроллера на уровне действия и сделать метод доступным для анонимных пользователей

Вы не указали свою версию MVC, поэтому я предполагаю последнее на сегодняшний день (MVC 4.5). Однако это не изменит ответ, даже если вы используете MVC 3.

[Anonymous] переопределяет контроллер [Authorize] (случай 3)

Случай 3. Мне не нужно покрывать (использование [AllowAnonymous]), как было сказано во всем SO и по всему Интернету. Достаточно сказать: если вы укажете [AllowAnonymous] в действии, оно сделает это действие общедоступным, даже если на нем есть [Authorize].

Вы также можете сделать весь веб-сайт предметом авторизации с использованием глобального фильтра и использовать AllowAnonymous для нескольких действий или контроллеров, которые вы хотите опубликовать.

[Authorize] является аддитивным (случай 1)

Случай 1 прост. В качестве примера возьмем следующий контроллер:

[Authorize(Roles="user")]
public class HomeController : Controller {
    public ActionResult AllUsersIndex() {
        return View();
    }

    [Authorize(Roles = "admin")]
    public ActionResult AdminUsersIndex() {
        return View();
    }
}

По умолчанию [Authorize(Roles="user")] делает все действия в контроллере доступными для учетных записей только в роли пользователя. Поэтому для доступа к AllUsersIndex вы должны быть в роли пользователя. Однако для доступа к AdminUsersIndex вы должны быть как в роли "пользователь", так и "admin". Например:

  • Имя пользователя: Боб, Роли: пользователь, не может получить доступ AdminUsersIndex, но может получить доступ к AllUsersIndex
  • Имя пользователя: Jane, Роли: admin, не может доступ AdminUsersIndex или AllUsersIndex
  • Имя пользователя: Tim, Роли: пользователь и admin, может получить доступ AdminUsersIndex и AllUsersIndex

Это иллюстрирует, что атрибут [Authorize] является аддитивным. Это также относится к свойству Users этого атрибута, который можно комбинировать с Roles, чтобы сделать его еще более ограничивающим.

Это поведение связано с тем, как работают атрибуты контроллера и действия. Атрибуты соединены вместе и применяются в контроллере заказа, а затем в действии. Если первая отказывается от авторизации, тогда возвращаются элементы управления и атрибут действия. Если первая проходит авторизацию, тогда также проверяется вторая. Вы можете переопределить этот порядок, указав Order (например [Authorize(Roles = "user", Order = 2)]).

Переопределение [Authorize] (случай 2)

Случай 2 сложнее. Напомним, что атрибуты [Authorize] проверяются в порядке (Global then) Controller, затем Action. Первый, чтобы обнаружить, что пользователь не может быть допущен к авторизации, другие не вызываются.

Один из способов - определить два новых атрибута, как показано ниже. [OverrideAuthorize] не делает ничего, кроме как отложить до [Authorize]; его единственная цель - определить тип, который мы можем проверить. [DefaultAuthorize] позволяет нам проверить, украшено ли действие, вызываемое в запросе, [OverrideAuthorize]. Если это так, мы отложим проверку проверки действия, иначе мы продолжим проверку уровня контроллера.

public class DefaultAuthorizeAttribute : AuthorizeAttribute {
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        var action = filterContext.ActionDescriptor;
        if (action.IsDefined(typeof(OverrideAuthorizeAttribute), true)) return;

        base.OnAuthorization(filterContext);
    }
}
public class OverrideAuthorizeAttribute : AuthorizeAttribute {
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);
    }
}

Затем мы можем использовать его следующим образом:

[DefaultAuthorize(Roles="user")]
public class HomeController : Controller {
    // Available to accounts in the "user" role
    public ActionResult AllUsersIndex() {
        return View();
    }
    // Available only to accounts both in the "user" and "admin" role
    [Authorize(Roles = "admin")]
    public ActionResult AdminUsersIndex() {
        return View();
    }
    // Available to accounts in the "superuser" role even if not in "user" role
    [OverrideAuthorize(Roles = "superuser")]
    public ActionResult SuperusersIndex() {
        return View();
    }
}

В приведенном выше примере SuperusersIndex доступен для учетной записи, которая имеет роль "суперпользователя", даже если она не имеет роли "пользователя".

Ответ 2

Я хотел бы добавить что-то к Overriding [Authorize] (case 2)

OverrideAuthorizeAttribute и DefaultAuthorizeAttribute работают нормально, но я обнаружил, что вы также можете использовать OverrideAuthorizationAttribute, который переопределяет фильтры авторизации, определенные на более высоком уровне.

[Authorize(Roles="user")]
public class HomeController : Controller {
    // Available to accounts in the "user" role
    public ActionResult AllUsersIndex() {
        return View();
    }
    // Available only to accounts both in the "user" and "admin" role
    [Authorize(Roles = "admin")]
    public ActionResult AdminUsersIndex() {
        return View();
    }
    // Available to accounts in the "superuser" role even if not in "user" role
    [OverrideAuthorization()]
    [Authorize(Roles = "superuser")]
    public ActionResult SuperusersIndex() {
        return View();
    }
}

Ответ 3

Если использовать его на контроллере, тогда будут обработаны все методы этого контроллера.

[Authorize]
public class SomeController(){

    // all actions are effected
    public ActionResult Action1
    public ActionResult Action2

Если вы хотите предотвратить одно из этих действий, вы можете использовать что-то вроде этого:

[Authorize]
public class SomeController(){

    // all actions are effected
    public ActionResult Action1
    public ActionResult Action2

    [AllowAnonymous]
    public ActionResult Action3 // only this method is not effected...

Ответ 4

Я сделал адаптацию этого ответа во втором случае для ASP.NET Core 2.1.

Разница с ASP.NET Core AuthorizeAttribute заключается в том, что вам не нужно вызывать базовый метод AuthorizeAttribute.OnAuthorization для перехода к обычной авторизации. Это означает, что даже если вы явно не вызываете базовый метод, базовый AuthorizeAttribute может по-прежнему закорачивать авторизацию, запретив доступ.

Я сделал то, что я создал DefaultAuthorizeAttribute, который наследуется не от AuthorizeAttribute, а от Attribute. Поскольку DefaultAuthorizeAttribute не наследуется от AuthorizeAttribute, мне пришлось воссоздать поведение авторизации.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class DefaultAuthorizeAttribute : Attribute, IAuthorizationFilter
{
    private readonly AuthorizeFilter m_authorizeFilter;

    public DefaultAuthorizeAttribute(params string[] authenticationSchemes)
    {
        var policyBuilder = new AuthorizationPolicyBuilder()
            .AddAuthenticationSchemes(authenticationSchemes)
            .RequireAuthenticatedUser();
        m_authorizeFilter = new AuthorizeFilter(policyBuilder.Build());
    }

    public void OnAuthorization(AuthorizationFilterContext filterContext)
    {
        if (filterContext.ActionDescriptor is ControllerActionDescriptor controllerAction
            && controllerAction.MethodInfo.GetCustomAttributes(typeof(OverrideAuthorizeAttribute), true).Any())
        {
            return;
        }
        m_authorizeFilter.OnAuthorizationAsync(filterContext).Wait();
    }
}

public class OverrideAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext filterContext) { }
}