Если у меня есть атрибут Authorize как на контроллере, так и на действии, какой из них будет действовать? Или оба будут действовать?
Asp.net MVC4: авторизация как для контроллера, так и для действий
Ответ 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) { }
}