Фильтр авторизации

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core

Последнее обновление: 31.10.2015

Фильтры авторизации отрабатывают до запуска остальных фильтров и вызова методов действий. Они реализуют интерфейс IAuthorizationFilter:

using System.Web.Mvc
{
    public interface IAuthorizationFilter
    {
        void OnAuthorization(AuthorizationContext filterContext);
    }
}

И если при получении запроса окажется, что к запрашиваемому действию контроллера применяется данный фильтр, то сначала срабатывает метод OnAuthorization данного интерфейса. И если фильтр одобрит запрос, то далее вызывается действие. Иначе действие не будет работать.

Для понимания работы фильтров авторизации рассмотрим стандартное приложение MVC по шаблону Internet Application, которое по умолчанию содержит всю логику авторизации и применение этого фильтра. В рамках данного раздела мы не будем подробно останавливаться на механизмах авторизации и аутентификации, лишь рассмотрим их в той мере, в какой они касаются фильтров авторизации.

Во-первых, откроем файл Web.config. Где-то в районе 22-23 строки вы увидите настройки аутентификации (Часто путают такие понятия как аутентификация и авторизация. Аутентификация представляет верификацию пользователя с помощью механизма ввода логина (имени пользователя/пароля и т.д.), а авторизация - это верификация тех действий, которые пользователь может совершать в отношении сайта. Обычно с авторизацией связывают систему разделения ролей.):

   <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" timeout="2880" />
    </authentication>

В данном случае указывается, что будет использоваться аутентификация форм. А в качестве пути, по которому можно произвести авторизацию на сайте, будет использоваться путь /Account/Login - то есть авторизовать вас или нет будет решать метод Login контроллера Account.

Атрибут timeout указывает время в минутах, которое пользователь может быть авторизован на сайте после успешной аутентификации.

Кроме аутентификации форм существует аутентификация Windows, однако ее можно использовать преимущественно в приложениях по шаблону Intranet Application, где для идентификации пользователей используются учетные записи Windows. Однако к Internet Application аутентификация Windows не применяется.

Также для аутентификации и авторизации нам необходима модель, с которой бы сопоставлялись учетные записи пользователей. В папке models вы можете увидеть файл AccountModels.cs, который по умолчанию содержит уже три модели - для регистрации (RegisterModel), логина (LoginModel) и смены пароля (ChangePasswordModel).

Весь процесс аутентификации выполняется контроллером AccountController, к которому применяется фильтр авторизации. Рассмотрим действие Login, которое и предназначено для аутентификации пользователя:

[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
    ViewBag.ReturnUrl = returnUrl;
    return View();
}

//
// POST: /Account/Login

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
    if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
    {
                return RedirectToLocal(returnUrl);
    }

    // If we got this far, something failed, redisplay form
    ModelState.AddModelError("", "The user name or password provided is incorrect.");
    return View(model);
}

Атрибут AllowAnonymous

Во-первых, обратите внимание на атрибут AllowAnonymous, который установлен для большинства методов контроллера. Поскольку у нас к контроллеру применяется атрибут Authorize, то фреймворк сначала будет смотреть, есть ли у пользователя необходимые права для доступа к методу контроллера. Однако в этом случае мы попадаем в замкнутый круг - чтобы авторизоваться, надо обратиться к методу Login, но чтобы обратиться к этому методу, уже надо быть авторизованным. Поэтому применяется атрибут AllowAnonymous, который открывает публичный доступ к методу контроллера.

Аутентификация

Перейдем к POST-методу, где мы получаем введенные данные логина и пароля: первая часть конструкции if (ModelState.IsValid осуществляет валидацию полученной модели. А вторая часть конструкции - метод WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe) получает логин и пароль пользователя и сравнивает их с теми пользователями, которые уже имеются. И если пользователь с такими данными существует, то пользователь аутентифицируется, и устанавливаются куки.

Если вы запустите приложение, вы можете зарегистрировать нового пользователя. И потом уже от его имени осуществлять вход на сайт.

Однако у нас пока по сути фильтр авторизации не применяется ни к чему, кроме контроллера AccountController. Поэтому откроем контроллер HomeController и к методу Index применим атрибут Authorize:

[Authorize]
public ActionResult Index()
{
	.............
}

В результате после запуска приложения мы попадем не на представление Index.cshtml, а на представление Login.cshtml. Так как фреймворк видит, что мы не аутентифицированы , и поэтому будет перенаправлять нас на метод Login контроллера Account, как определено в файле конфигурации проекта.

Лишь после удачного входа на сайт нам станет доступен ресурс Home/Index.

Взаимодействие с БД

Теперь еще один интересный вопрос - где это все сохраняется? Если вы посмотрите на файл конфигурации, то можете увидеть в секции connectionStrings настройки всех возможных подключений проекта к базам данных. По умолчанию создается подключение DefaultConnection. В моем случае оно выглядит так для проекта с названием TestMvcApplication, создаваемого в Visual Studio 2012:

<connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=aspnet-TestMvcApplication-20130423121813;Integrated Security=SSPI;
			AttachDBFilename=|DataDirectory|\aspnet-TestMvcApplication-20130423121813.mdf" providerName="System.Data.SqlClient" />
  </connectionStrings>

Опреlеление строки подключения для Visual Studio 2010 выглядело бы примерно так:

<connectionStrings>
    <add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=aspnet-Mvc4InternetApplication-20120828133008;Integrated Security=SSPI" />
  </connectionStrings>

В Visual Studio 2012 при использовании шаблона Internet Application, содержащего инфраструктуру для работы с учетными записями, используется подход CodeFirst - то есть при первом обращении к моделям создается база данных в папке App_Data. Мы можем просмотреть БД, перейдя в окно Database Explorer (либо визуально мы модем увидеть ее в проводнике).

По имеющимся моделям в базе создается несколько таблиц, которые предназначены для хранения учетных данных, как пользователи, роли и т.д.

Таблица UserProfile содержит список пользователей, зарегистрированных для данного приложения.

Учетные данные для каждого пользователя, как например, пароль и другие, хранятся в таблице webpages_Membership, а таблица webpages_Roles содержит роли, определенные в системе. По умолчанию таблица не содержит каких-либо ролей, поэтому мы можем добавить их:

Затем поскольку непонятно, какой пользователь какую роль в системе выполняет, нам надо связать роли и пользователей. Для этого откроем таблицу webpages_UsersInRoles и сопоставим id пользователей с id ролей:

После этого мы можем использовать роли в приложении.

(В Visual Studio 2010 для определения и назначения ролей можно воспользоваться средством конфигурации ASP.NET Configuration, которое можно запустить, выбрав в меню Visual Studio пункт Project->ASP.NET Configuration)

Использование ролей при авторизации

Авторизация в mvc обладает большой гибкостью, так как мы можем настроить доступ к ресурсам сайта для отдельных пользователей или ролей. Для этого мы можем использовать два свойства атрибута AuthorizeAtribute:

  • Users - содержит перечисление имен пользователей, которым разрешен вход

  • Roles - содержит перечисление имен ролей, которым разрешен вход

Использование свойств Users и Roles:

[Authorize (Users="eugene, sergey")]
public ActionResult Index()
{
	.............
}
[Authorize (Roles="admin")]
public ActionResult Create()
{
	.............
}
[Authorize (Roles="admin, moderator", Users="eugene, sergey")]
public ActionResult Edit()
{
	.............
}

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

Создание собственного фильтра авторизации

Выше мы посмотрели, как работает фильтр авторизации. Однако за кадром остались принципы работы данного фильтра. Для этого мы создадим свой фильтр.

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

Наиболее простой и безопасный способ создания своего фильтра авторизации - это наследование класса от атрибута AuthorizeAtribute и переопределение метода AuthorizeCore:

using System;
using System.Web;
using System.Web.Mvc;

namespace Mvc4InternetApplication.Filters
{
    public class MyAuthorizedAttribute : AuthorizeAttribute
    {
        private string[] allowedUsers;
        private string[] allowedRoles;

        public MyAuthorizeAttribute(string[] users, string[] roles)
        {
            allowedUsers = users;
            allowedRoles = roles;
        }

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            return httpContext.Request.IsAuthenticated && 
                allowedUsers.Contains(httpContext.User.Identity.Name) && 
                Role(httpContext);
        }

        private bool Role(HttpContextBase httpContext)
        {
            if (allowedRoles.Length > 0)
            {
                for (int i = 0; i < allowedRoles.Length; i++)
                {
                    if (httpContext.User.IsInRole(allowedRoles[i]))
                        return true;
                }
                return false;
            }
            return true;
        }
    }
}

Массивы пользователей и ролей помогают провести детальную аутентификацию и авторизацию. (Хотя, как вы видели, у атрибута уже есть свойства Users и Roles.) С помощью объекта контекста HttpContextBase мы получаем все данные запроса и ответа, а также устанавливаем, является ли запрос аутентифицированным. Применение фильтра подобно применению стандартной версии:

[MyAuthorize (Roles="admin, moderator", Users="eugene, sergey")]
public ActionResult Edit()
{
	.............
}

Переопределение атрибута AuthorizeAtribute наиболее безопасный способ создания механизма авторизации, по сравнению, скажем, с реализацией в своем классе интерфейса IAuthorizationFilter. Поэтому в случаях, когда вам надо создать свой класс атрибута авторизации, рекомендуется именно этот способ.

Хотя в предыдущем примере переопределение по сути не имело смысла, однако все-таки может потребоваться реализация своего атрибута. Например, чтобы администраторам сервера сайта не приходилось каждый раз набирать пароль при входе на сайт, мы можем сделать следующую реализацию атрибута:

public class LocalAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        return httpContext.Request.IsLocal || base.AuthorizeCore(httpContext);
    }
}

И также использовать роли и пользователей в приложении:

[LocalAuthorize (Roles="admin", Users="eugene, sergey")]
public ActionResult Edit()
{
	.............
}

Однако в этом случае заходить на сайт смогут не только указанные роли и пользователи, но и локальные пользователи для данной машины (что мы узнаем с помощью свойства httpContext.Request.IsLocal).

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850