ASP.NET MVC / [Из песочницы] Динамическая проверка прав доступа для ASP.NET MVC

в 19:00, , рубрики: asp.net mvc, авторизация, метки: , ,

ASP.NET MVC / [Из песочницы] Динамическая проверка прав доступа для ASP.NET MVC
В ASP.NET MVC есть встроенная возможность ограничить доступ к тем или иным контроллерам и их действиям. Эта возможность предоставляется атрибутом AuthorizeAttribute, но возможностей и гибкости ему явно не хватает (точнее их практически нет). Права можно определить только на этапе разработки и без повторной компиляции никак не изменить. А ведь создать собственный атрибут, обладающий необходимым функционалом совсем не сложно.
Итак, приступим. Создаём новый проект в Visual Studio, тип выбираем ASP.NET MVC 3 Web Application, называем DynamicAuthorize. Ждём, пока студия сгенерирует проект.
Каким образом хранить и определять права доступа можно различными способами: в БД, получать с удалённого сервиса, в xml-файле и т.д. Всё зависит от задачи и вашего предпочтения. Для примера же, чтобы не отвлекаться на реализации этих механизмов сделаем класс, возвращающий сведения о разрешениях, заменить его на нужную вам реализацию, думаю проблем не вызовет. Собственно класс PermissionManager:
public class PermissionManager
{
public bool ValidatePermissions(string controller, string action, string user)
{
bool isUserAccess = false;

if (user == "user1" && controller == "Home")
{
switch (action)
{
case "Test":
isUserAccess = true;
break;
}
}

if (user == "user2" && controller == "Home")
{
switch (action)
{
case "Edit":
isUserAccess = true;
break;
}
}

// Незарегистрированных ползователей пускаем на главную и "О проекте"
if (controller == "Home" && (action == "Index" || action == "About"))
{
isUserAccess = true;
}

return isUserAccess;
}
}

Класс элементарен, поэтому объяснять что он делает, думаю не стоит. Что же касается непосредственно авторизации, то в MVC есть интерфейс IAuthorizationFilter, в котором определён единственный метод OnAuthorization. Этот метод вызывается при необходимости авторизовать пользователя, т.е. проверить имеет ли он права на данную операцию. Это как раз то, что нам нужно. Ну хватит теории, приступаем к созданию непосредственно атрибута, т.е. класса DynamicAuthorizeAttribute:
public class DynamicAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
PermissionManager permissionManager = new PermissionManager();
string action = filterContext.ActionDescriptor.ActionName;
string controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
string user = filterContext.HttpContext.User.Identity.Name;

if (!permissionManager.ValidatePermissions(controller, action, user))
{
throw new UnauthorizedAccessException("Пользователю не разрешено использование данного действия");
}
}
}

В передаваемом параметре типа AuthorizationContext имеется множество полезных свойств, дающих возможность организовать проверку прав доступа множеством способов, но в данном случае проверка элементарна, мы просто воспользуемся методом класса PermissionManager.
На этом, собственно, создание динамической авторизации закончено. Я же говорил, что это не сложно. Ну что, приступаем к тестам. Создадим два дополнительных действия (два нам любезно создала студия) контроллера Home:
public ActionResult Test()
{
return View();
}

public ActionResult Edit()
{
return View();
}

Создаём для них представления (я воспользовался генератором Visual Studio)
И помечаем контроллер нашим, только что созданным атрибутом:
[Attributes.DynamicAuthorize]
public class HomeController : Controller

Добавляем в представление Index ссылки на созданные действия контроллера:

@Html.ActionLink("Test", "Test")

@Html.ActionLink("Edit", "Edit")

Теперь необходимо собрать проект, нажимаем Ctrl+Shift+B, должно получится без ошибок.
Теперь создадим тестовых пользователей, запускаем ASP.NET Admin tools
Переходим в раздел «Безопасность» и добавляем 2-х пользователей с именами user1 и user2. Всё можно запускать проект. Теперь если нажать на одну из ссылок не авторизовавшись, получите ошибку доступа. Если авторизоваться как user1, будет доступен действие Test, но не доступно Edit. Если зайти как user2, то всё наоборот.
В заключение хотел бы сказать, что несмотря на простоту реализации, у новичков часто возникает вопрос как организовать динамическую проверку прав доступа. Надеюсь этот пост поможет людям, столкнувшимся с этим вопросом, справиться с ним и сделать авторизацию именно такой, какой она была задумана.
Скачать проект можно здесь.

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js