Вы создали WebAPI и теперь хотите контролировать доступ к нему? В этой серии статей мы рассмотрим несколько вариантов защиты WebAPI от неавторизрованых пользователей. Серия будет охватывать обе стороны, и аутентификацию и авторизацию пользователей.
- Аутентификация — позволяет однозначно идентифицировать пользователя. Например, Алиса входит в систему со своим логином и паролем, и сервер использует эти данные для аутентификации Алисы.
- Авторизация решает может ли пользователь выполнить те или иные действия. Например, Алиса может иметь право на чтение ресурса, но не может создать новый ресурс.
Первая серия статей дает общий обзор аутентификации и авторизации в ASP.NET Web API. Другие статьи описывают общие сценарии аутентификации для WebAPI.
Аутентификация
WebAPI предполагает что аутентификацию проводит хост, в котором он размещается. Для веб-хостинга хостом является IIS, который использует HTTP модули для аутентификации. Вы можете сконфигурировать свой проект на использование модулей аутентификации встроенных в IIS или ASP.NET, или же написать собственный модуль HTTP, для выполнения кастомной проверки подлинности.
Когда хост проводит аутентификацию пользователя, он создает объект IPrincipal, который представляет собой контекст безопасности в котором исполняется код. Созданный объект прикрепляет к текущему потоку, и к нему можно обратиться через свойство Thread.CurrentPrincipal. Контекст безопасности содержит связанный с ним объект Identity, с информацией о пользователе. Если пользователь прошел аутентификацию, свойство Identity.IsAuthenticated вернет значение true. Для анонимных запросов свойство вернет false.
Использование HTTP обработчиков сообщений для аутентификации.
Вместо использования хоста, логику аутентификации можно поместить в обработчик HTTP сообщения. В этом случае, обработчик сообщения исследует HTTP запрос и сам задаст контекст безопасности.
Несколько примеров для чего может понадобиться аутентификация в обработчиках сообщений:
- HTTP модуль видит все запросы проходящие по каналу ASP.NET, обработчик видит только запросы предназначенные WebAPI
- Вы можете установить отдельные обработчики сообщений на каждый маршрут, применяя различные схемы аутентификации
- HTTP модули специфичны для IIS. Обработчики сообщений независимы от хоста, и могут быть использованы как в случае web-хостинга, так и при self-хостинге.
- HTTP модули учавствуют при логгинге IIS, аудите и т.д.
- HTTP модули запускаются раньше в цепочке прохождения запроса. Если проводите аутентификацию в обработчике сообщений, контекст безопасности не будет установлен пока не будет запущен обработчик. Кроме того, контекст безопасности возвращается к предыдущему состоянию когда ответ на запрос покинет обработчик сообщения.
В общем, если вы не собираетесь использовать self-хостинг, использование HTTP модулей аутентификации лучший вариант. В противном случае логику стоит вынести в обработчики сообщений.
Установка контекста безопасности
Если ваше приложение выполняет какую-либо логику связанную с аутентификацией, вы должны задать контекст безопасности в двух свойствах:
- Thread.CurrentPrincipal — стандартный путь задать контекст безопасности для потока в .NET
- HttpContext.Current.User — свойство специфичное для ASP.NET
Следующий код показывает как задать контекст безопасности:
private void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
Для веб-хостинга, вы должны задать контекст безопасности в обоих свойствах, иначе контекст безопасности может стать несогласованным. Для обеспечения независимости вашего кода от способа
Авторизация
В цепочке обработчиков запроса авторизация находится ближе к контроллеру. Это позволяет проводить тонкую настройку доступа к ресурсам.
- Фильтры авторизации отрабатывают до методов контроллера. Если запрос не авторизован, фильтр вернет сообщение об ошибке, а метод контроллера не будет вызван.
- В методе контроллера вы можете получить текущий контекст безопасности из свойства ApiController.User. Например, вы можете фильтровать список ресурсов в зависимости от имени пользователя, возвращая только доступные для него.
Использование аттрибута [Authorize]
WebAPI предоставляет встроенный фильтр авторизации, AuthorizeAttribute, этот фильтр проверяет авторизован ли пользователь. Если нет, фильтр вернет код состояния HTTP 401 (Не авторизован), без вызова метода.
Вы можете применять фильтр как глобально, так и на уровне контроллера или на уровне методов.
Фильтр на глобальном уровне: чтобы ограничить доступ для каждого контроллера WebAPI, добавьте фильтр AuthorizeAttribute в глобальный список фильтров:
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new AuthorizeAttribute());
}
Фильтр на уровне контроллера: для ограничения доступа к конкретному контроллеру, добавьте фильтр в качестве атрибута класса контроллера:
// Require authorization for all actions on the controller.
[Authorize]
public class ValuesController : ApiController
{
public HttpResponseMessage Get(int id) { ... }
public HttpResponseMessage Post() { ... }
}
Фильтр на уровне метода: для ограничения доступа к методу, добавьте к нему атрибут:
public class ValuesController : ApiController
{
public HttpResponseMessage Get() { ... }
// Require authorization for a specific action.
[Authorize]
public HttpResponseMessage Post() { ... }
}
Кроме того, в можете задать ограничение на контроллер, и разрешить анонимный доступ на отдельные методы при помощи атрибута [AllowAnonymous]. В следующем примере, доступ к методу Post ограничен, а метод Get доступен для анонимных вызовов:
[Authorize]
public class ValuesController : ApiController
{
[AllowAnonymous]
public HttpResponseMessage Get() { ... }
public HttpResponseMessage Post() { ... }
}
В предыдущих примерах, фильтр позволял получить доступ любому пользователю прошедшему проверку подлинности, запрещая доступ только анонимным пользователям. Вы также можете предоставить доступ конкретным пользователям или ролям:
// Restrict by user:
[Authorize(Users="Alice,Bob")]
public class ValuesController : ApiController
{
}
// Restrict by role:
[Authorize(Roles="Administrators")]
public class ValuesController : ApiController
{
}
Фильтр AuthorizeAttribute для контроллеров WebAPI находится в пространстве имен System.Web.Http. Существует аналогичный фильтр для контроллеров MVC в пространстве имен System.Web.Mvc. Этот тип несовместим с контроллерами WebAPI.
Кастомные фильтр авторизации
Кастомный фильтр должен быть унаследован от одного из следующих типов:
- AuthorizeAttribute. Наследуйте этот класс для реализации синхронной логики авторизации на основе текущего пользователя или роли пользователя.
- AuthorizationFilterAttribute. Данный класс пригодится для реализации логики авторизации необязательно основанной на пользователе или его роли.
- IAuthorizationFilter. Реализуйте этот интерфейс для асинхронной логики авторизации. Например ваша авторизация предполагает асинхронные или сетевые вызовы (если логика завязана на процессорных вычислениях, то лучше наследоваться от AuthorizationFilterAttribute, чтобы не писать асинхронные методы)
Следующая диаграмма показывает иерархию классов фильтров:
Авторизация внутри метода контроллера
В некоторых случаях можно разрешить выполнение запроса, но изменить поведение на основе контекста безопасности. Например, результат запроса зависит от роли пользователя. Внутри метода контроллера вы можете получить текущий контекст безопасности обратившись к свойству ApiController.User:
public HttpResponseMessage Get()
{
if (User.IsInRole("Administrators"))
{
// ...
}
}
* Перевод выполнен в вольном стиле, возможно кого-то покоробят англицизмы наподобие 'кастомный', но эти слова стали частью it жаргона, и русский перевод не воспринимается как должно.
Автор: Ogoun