Использование атрибута [AllowAnonymous] в ASP.NET MVC 4

Предположим, необходимо создать сайт с содержимым, доступным только для зарегистрированных пользователей. При этом есть несколько страниц, которые могут просматривать все. Давайте рассмотрим как новые возможности ASP.NET MVC 4 упрощают решение такой задачи.

Решение для ASP.NET MVC 3

Разумеется, речь подойдёт об использовании только возможностей "из коробки". Поэтому, в ASP.NET MVC 3, чтобы закрыть незарегистрированным пользователям доступ к сайту, необходимо отметить почти каждое Действие атрибутом [Authorize]. Исключение составляют регистрация, авторизация и еще ряд в зависимости от задач сайта.

Такой подход несет в себе потенциальный источник опасности. Ведь при большом количестве страниц можно было просто забыть указать атрибут авторизации.

Вариант для ASP.NET MVC 4 и использование [AllowAnonymous]

Начиная с ASP.NET MVC 4 есть возможность решить поставленную задачу проще и, самое главное, надежнее.

Для примера создадим тестовый проект AllowAnonymousDemo на базе стандартного ASP.NET MVC 4 Internet Application. Затем добавим AuthorizeAttribute как глобальный фильтр (файл global.asax.cs):

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
    filters.Add(new System.Web.Mvc.AuthorizeAttribute());
}

Теперь для просмотра любой страницы сайта пользователю потребуется предварительно ввести свои имя пользователя и пароль. Вроде все хорошо и нет необходимости помнить о добавлении атрибута [Authorize] для каждого Действия. Однако, здесь возникает новая проблема, из-за которой такое решение не использовалось в ASP.NET MVC 3. Ведь получается, что даже для регистрации и самой авторизации необходимо предварительно авторизоваться. Вроде бы как замкнутый круг.

Для решения этой задачи в ASP.NET MVC 4 появился новый атрибут [AllowAnonymous]. Он разрешает  публичный доступ к отдельным Действиям при глобально установленном AuthorizeAttribute. Т.е. теперь вместо всех Действий, необходимо отметить их очень ограниченный список.

Обратите внимание, что в стандартном шаблоне ASP.NET MVC 4 Internet Application с помощью [AllowAnonymous] уже отмечены все необходимые Действия в AccountController (AccountController.cs, код методов убран для краткости).

// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login()
{
    .........
}

// POST: /Account/JsonLogin
[AllowAnonymous]
[HttpPost]
public JsonResult JsonLogin(LoginModel model, string returnUrl)
{
    .........
}

// POST: /Account/Login
[AllowAnonymous]
[HttpPost]
public ActionResult Login(LoginModel model, string returnUrl)
{
    .........
}

// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
    .........
}

// POST: /Account/JsonRegister
[AllowAnonymous]
[HttpPost]
public ActionResult JsonRegister(RegisterModel model)
{
    .........
}

// POST: /Account/Register
[AllowAnonymous]
[HttpPost]
public ActionResult Register(RegisterModel model)
{
    .........
}

Аналогично, можно разрешить всем посетителям сайта просматривать, например, страницу контактов:

[AllowAnonymous]
public ActionResult Contact()
{
    ViewBag.Message = "Your quintessential contact page.";

    return View();
}

Таким образом, для решения поставленной задачи, в ASP.NET 4.0 необходимо выполнить два действия:

  1. Установить глобально AuthorizeAttribute в методе RegisterGlobalFilters() (файл global.asax.cs).
  2. Указать атрибут [AllowAnonymous] для Действий, относящихся к страницам которые должны быть доступны в��ем посетителям сайта.

Комментарии (8) -

Николай 29.03.2012 15:38:51

Ну не очень удобно к каждому экшену вешать этот атрибут, если надо, например, "спрятать весь контроллер".

public class AuthPermission : ActionFilterAttribute

    {
        string RolePerm;

        /// <summary>
        /// Attribute blocks anonymous users to run actions of controller.
        /// </summary>
        public AuthPermission()
        {
            RolePerm = "";
        }

        /// <summary>
        /// Attribute blocks anonymous users and user not in Role to run actions of controller.
        /// </summary>
        public AuthPermission(string Role)
        {
            RolePerm = Role;
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);

            if (filterContext.HttpContext.User.Identity.IsAuthenticated)
            {
                if (RolePerm == "") { return; }
            }

            if (filterContext.HttpContext.User.IsInRole(RolePerm)) { return; }            

            filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new
            {
                controller = "Account",
                action = "Login"
            }));
        }
    }


Это я набросал атрибут, который вешается на контроллер и там есть перегрузка для роли, которой будет доступен контроллер, например, только админу.

    [AuthPermission("admin")]

    public class AdminController : Controller
    {
        ...

@ Николай: Не совсем понял о чем вы. Если о "спрятать весь контроллер", то AuthorizeAttribute прекрасно назначается на класс. Если о ролях, то у него есть свойство Roles.

Человек свой велосипед изобрел Smile

@ Николай: При установленном глобально AuthorizeAttribute ваше решение работать не будет.

Константин 06.04.2012 14:18:18

новый атрибут конечно удобный, но и в mvc 3 особых проблем без него нет.
можно создать контроллер

[Authorize]
public class AuthorizeController : Controller
{
}

от него наследовать все контроллеры, действия в которых закрыты авторизацией.
А действия, которые не закрыты авторизацией делать в контроллерах, которые не наследуются от AuthorizeController

@ Константин: Так вопрос как-раз в том, что иногда необходимо в одном Контроллере разделить Действия на выполняемые авторизованным пользователем и неавторизованным. Хороший пример как раз AccountController.

Николай 07.06.2012 19:24:27

Smile я gпонял в чем дело.

/// <summary>

    /// Attribute blocks anonymous users to run actions of controller.
    /// </summary>
    public class AuthPermission : ActionFilterAttribute
    {
        string RolePerm = null;
        string lContr = "Admin", lAction = "Login";
        /// <summary>
        /// Attribute blocks anonymous users to run actions of controller.
        /// </summary>
        public AuthPermission()
        {
            RolePerm = null;
        }

        /// <summary>
        /// Attribute blocks anonymous users and user not in Role to run actions of controller.
        /// </summary>
        public AuthPermission(string Role)
        {
            RolePerm = Role;
        }

        public AuthPermission(string LoginAction, string LoginController)
        {
            RolePerm = null;
        }

        public AuthPermission(string Role, string LoginAction, string LoginController)
        {
            RolePerm = Role;
            lAction = LoginAction;
            lContr = LoginController;
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);

            if (filterContext.RouteData.Values["controller"].ToString() == lContr && filterContext.RouteData.Values["action"].ToString() == lAction) { return; }

            if (filterContext.HttpContext.User.Identity.IsAuthenticated)
            {
                if (RolePerm == null) { return; }
            }

            if (filterContext.HttpContext.User.IsInRole(RolePerm)) { return; }            

            filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new
            {
                controller = lContr,
                action = lAction
            }));
        }
    }


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

Спасибо, помогло. То что искал

Добавить комментарий