Asp.net MVC4: авторизация как на контроллере, так и на действии
Если у меня есть атрибут Authorize и на контроллере, и на действии, какой из них будет действовать? Или оба вступят в силу?
3 ответа
Вы спрашивали:
Если у меня есть атрибут Authorize и на контроллере, и на действии, какой из них будет действовать? И то и другое?
Чтобы ответить на это просто: оба. Эффект заключается в AND
два ограничения вместе. Я объясню, почему ниже...
подробности
Итак, есть несколько причин, по которым вы могли бы спросить об этом.
- Вы хотите знать, как применить дополнительное ограничение к действию по сравнению с методом. например
- На уровне контроллера установите пользователей в роли "пользователь"
- На уровне действий дополнительно принудительно применяйте пользователей в роли "администратор"
- Вы хотите заменить ограничение контроллера на уровне действия
- Вы хотите удалить ограничение контроллера на уровне действия и сделать метод доступным для анонимных пользователей.
Вы не указали свою версию 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
Вы должны быть как в роли пользователя, так и в роли администратора. Например:
- Имя пользователя: Боб, Роли: пользователь, не может получить доступ
AdminUsersIndex
, но может получить доступAllUsersIndex
- Имя пользователя: Джейн, Роли: администратор, не может получить доступ
AdminUsersIndex
или жеAllUsersIndex
- Имя пользователя: Тим, Роли: пользователь и администратор, может получить доступ
AdminUsersIndex
а такжеAllUsersIndex
Это показывает, что [Authorize]
Атрибут аддитивен. Это также верно для Users
свойство атрибута, которое можно комбинировать с Roles
чтобы сделать это еще более ограничительным.
Такое поведение связано с тем, как работают атрибуты контроллера и действия. Атрибуты объединяются и применяются в контроллере заказа, а затем в действии. Если первый отказывает в авторизации, управление возвращается и атрибут действия не вызывается. Если первый проходит авторизацию, то второй также проверяется. Вы можете переопределить этот порядок, указав Order
(например [Authorize(Roles = "user", Order = 2)]
).
Переопределение [Authorize]
(случай 2)
Случай 2 сложнее. Напомним, что [Authorize]
Атрибуты проверяются в порядке (Глобальный затем) Контроллер, затем Действие. Первый, который обнаружит, что пользователь не имеет права быть авторизованным, выигрывает, остальные не получают вызова.
Одним из способов решения этой проблемы является определение двух новых атрибутов, как показано ниже. [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
доступен для учетной записи с ролью "суперпользователь", даже если у нее нет роли "пользователь".
Я хотел бы добавить что-то в 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();
}
}
Если использовать его на контроллере, то все методы этого контроллера будут применены.
[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...
Я сделал адаптацию второго случая этого ответа для 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) { }
}