Главная страница   /   16.3. Использование фильтров авторизации (ASP.NET MVC 4

ASP.NET MVC 4

ASP.NET MVC 4

Адам Фриман

16.3. Использование фильтров авторизации

Фильтры авторизации - это фильтры, которые запускаются первыми, перед любым другим фильтром или методом действия. Как следует из названия, эти фильтры осуществляют вашу политику авторизации, гарантируя, что методы действий могут быть вызваны только пользователями, имеющими право доступа. Фильтры авторизации реализуют интерфейс IAuthorizationFilter, показанный в листинге 16-6.

Листинг 16-6: Интерфейс IAuthorizationFilter
namespace System.Web.Mvc
{
	public interface IAuthorizationFilter
	{
		void OnAuthorization(AuthorizationContext filterContext);
	}
}

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

Предупреждение. Не пишите свой код безопасности!

Мы никогда не пишем собственный код безопасности. История программирования полна обломков приложений, разработчики которых думали, что могут написать хороший код безопасности. На самом деле, это - очень редкий навык. Как правило, остается какая-нибудь забытая или непротестированная мелочь, которая и становится зияющей дырой в безопасности приложения. Если вы нам не верите, просто загуглите фразу ошибка безопасности (или security bug) и просмотрите топ-результаты.

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

Более безопасный подход - создать подкласс класса AuthorizeAttribute, который будет содержать самый сложный код и облегчит написание пользовательского кода авторизации. Чтобы это продемонстрировать, мы создадим пользовательский фильтр. Добавим в проект папку Infrastructure и создадим в нем класс под названием CustomAuthAttribute.cs. Содержимое этого файла показано в листинге 16-7.

Листинг 16-7: Пользовательский фильтр авторизации
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Filters.Infrastructure
{
	public class CustomAuthAttribute : AuthorizeAttribute
	{
		private bool localAllowed;
		public CustomAuthAttribute(bool allowedParam)
		{
			localAllowed = allowedParam;
		}
		protected override bool AuthorizeCore(HttpContextBase httpContext)
		{
			if (httpContext.Request.IsLocal)
			{
				return localAllowed;
			}
			else
			{
				return true;
			}
		}
	}
}

Это простой фильтр авторизации. Он позволяет блокировать доступ к локальным запросам (локальный запрос осуществляется, когда браузер и сервер приложений запущены на одном устройстве; например, это компьютер, на котором вы разрабатываете приложение).

Мы использовали самый простой подход к созданию фильтра авторизации, который заключается в создании подкласса класса AuthorizeAttribute и переопределении метода AuthorizeCore. Благодаря этому мы сможем пользоваться функциями, встроенными в AuthorizeAttribute. Конструктор нашего фильтра принимает значение bool, указывающее, разрешены ли локальные запросы.

Самое интересное в нашем классе фильтра касается реализации метода AuthorizeCore, с помощью которого MVC Framework проверяет, авторизирует ли фильтр доступ для запроса. Аргументом этого метода является объект HttpContextBase, через который мы получаем информацию об обработке запроса. Используя преимущества встроенных функций базового класса AuthorizeAttribute, мы можем сосредоточиться на логике авторизации и вернуть из метода AuthorizeCore true, если хотим авторизировать запрос, и false, если нет.

Упрощаем атрибуты авторизации

В метод AuthorizeCore передается объект HttpContextBase, который обеспечивает доступ к информации о запросе, но не о контроллере или методе действия, к которому был применен атрибут авторизации. Основная причина, по которой разработчики реализуют интерфейс IAuthorizationFilter напрямую, - это получить доступ к AuthorizationContext, который передается в метод OnAuthorization. Из него можно получить гораздо больше информации, в том числе о маршрутизации и текущем контроллере и методе действия.

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

Применяем пользовательский фильтр авторизации

Чтобы использовать пользовательский фильтр авторизации, мы просто применяем атрибут к методам действий или контроллерам, которые мы хотим защитить, как показано в листинге 16-8. Здесь показано применение фильтра к методу действия Index контроллера Home.

Листинг 16-8: Применяем пользовательский фильтр авторизации
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Filters.Infrastructure;

namespace Filters.Controllers
{
	public class HomeController : Controller
	{
		[CustomAuth(false)]
		public string Index()
		{
			return "This is the Index action on the Home controller";
		}
	}
}

Мы установили значение false для аргумента конструктора, что означает, что локальным запросам будет отказано в доступе к методу действия Index. Чтобы проверить это, запустите приложение - когда браузером будет запрошен корневой URL, конфигурация маршрутизации направит нас на метод действия Index. Если отправляющий запрос браузер работает на той же машине, что и Visual Studio, то вы увидите результат, изображенный на рисунке 16-2. Фильтр авторизирует запрос, если он поступит от браузера, работающего на другом компьютере (или если мы присвоим аргументу конструктора фильтра значение true и перезапустим приложение).

Рисунок 16-2: Пользовательский фильтр авторизации отказывает в доступе локальным запросам

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

Хотя мы использовали класс AuthorizeAttribute как основу для пользовательского фильтра, у него есть своя собственная реализация метода AuthorizeCore, который используется для выполнения общих задач авторизации.

Используя AuthorizeAttribute напрямую, мы можем определить правила авторизации с помощью двух доступных свойств этого класса, как показано в таблице 16-2.

Таблица 16-2: Свойства AuthorizeAttribute
Название Тип Описание
Users string Разделенный запятыми список имен пользователей, которым разрешен доступ к методу действия.
Roles string Разделенный запятыми список названий ролей. Чтобы получить доступ к методу действия, пользователь должен обладать по крайней мере одной из этих ролей.

В листинге 16-9 показано, как можно использовать эти встроенные фильтры и их свойства для защиты метода действия.

Листинг 16-9: Используем встроенный фильтр авторизации
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Filters.Infrastructure;
namespace Filters.Controllers
{
	public class HomeController : Controller
	{
		[Authorize(Users = "adam, steve, jacqui", Roles = "admin")]
		public string Index()
		{
			return "This is the Index action on the Home controller";
		}
	}
}

В листинге мы указали как пользователей, так и роли. Это означает, что авторизация будет пройдена, только если оба условия будут выполнены: имя пользователя - adam, steve или jacqui и пользователь является администратором. По умолчанию, каждый запрос будет аутентифицирован. Если мы не указываем имена пользователей или роли для аутентификации, то любой авторизованный пользователь сможет использовать метод действия.

Подсказка

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

Для большинства приложений достаточно правил авторизации, которые обеспечивает AuthorizeAttribute. Если вы хотите реализовать что-то особенное, то можете наследовать этот класс, как мы делали ранее в этой главе.