Фильтры добавляют в процесс обработки запроса контроллером дополнительную логику. В Web API определены все те же виды фильтров, что и в MVC 5 с аналогичным действием:
Фильтры аутентификации: представляет интерфейс IAuthenticationFilter
. Определяет,
аутентифицирован ли клиент. Данный фильтр запускается до выполнения любого другого фильтра или метода действий
Фильтры авторизации: представляет интерфейс IAuthorizationFilter
. Определяет, имеет ли пользователь права доступа к данному ресурсу.
Данный фильтр запускается после фильтра аутентификации, но до любого другого фильтра или метода действия
Фильтры действий: представляет интерфейс IActionFilter
. Применяется к методу до и после обработки запроса
Фильтры переопределений: представляет интерфейс IOverrideFilter
. Переопределяет действие других фильтров по отношению к методу
Фильтры исключений: представляет интерфейс IExceptionFilter
. Используется для обработки исключений, которые возникают в методе при обработке запроса
Но в отличие от фильтров в MVC 5, фильтры в Web API отличаются тем, что реализуют интерфейс IFilter из пространства имен
System.Web.Http.Filters
:
public interface IFilter { bool AllowMultiple { get; } }
Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
Фильтры аутентификации проверяют, аутентифицирован ли пользователь. Если к методу применяется несколько фильтров, то фильтр аутентификации вызывается до всех остальных фильтров.
В основе всех фильтров аутентификаций лежит реализация интерфейса IAuthenticationFilter из пространства имен System.Web.Http.Filters
:
public interface IAuthenticationFilter : IFilter { Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken); Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken); }
Метод AuthenticateAsync()
вызывается до обработки запроса для идентификации пользователя, а метод ChallengeAsync()
вызывается уже когда начинается обработка для отправки ответа клиенту.
При рассмотрении аутентификации в Web API важно понимать ее отличие от ASP.NET MVC. Web Api не будет перенаправлять пользователя на страницу логина или ошибки, как нередко делается в MVC. Вместо этого клиенту будет отправляться статусный код HTTP 401 (Unauthorized).
Создадим свой фильтр. Для этого определим в проекте папку Filters и добавим в нее новый класс:
using System; using System.Collections.Generic; using System.Linq; using System.Net.Http.Headers; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web.Http.Filters; using System.Web.Http.Results; using System.Security.Principal; namespace WebApiApp.Filters { class CustomAuthenticationAttribute : Attribute, IAuthenticationFilter { public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken) { context.Principal=null; AuthenticationHeaderValue authentication = context.Request.Headers.Authorization; if (authentication != null && authentication.Scheme == "Basic") { string[] authData = Encoding.ASCII.GetString(Convert.FromBase64String(authentication.Parameter)).Split(':'); string[] roles = new string[] { "user" }; string login = authData[0]; context.Principal = new GenericPrincipal(new GenericIdentity(login), roles); } if (context.Principal == null) { context.ErrorResult = new UnauthorizedResult(new AuthenticationHeaderValue[] { new AuthenticationHeaderValue("Basic") }, context.Request); } return Task.FromResult<object>(null); } public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken) { return Task.FromResult<object>(null); } public bool AllowMultiple { get { return false; } } } }
Аутентифицированный пользователь в ASP.NET представлен объектом интерфейса IPrincipal. Контекст метода HttpAuthenticationContext хранит ссылку на
этот объект. С помощью свойства context.Request.Headers.Authorization
мы получаем заголовки авторизации, которые хранят
данные об авторизации: логин и пароль в зашифрованном виде с применением алгоритма Base64.
Чтобы получить логин и пароль, используем декодирование: Encoding.ASCII.GetString(Convert.FromBase64String(authentication.Parameter))
.
Результатом декодирования должен стать массив из двух элементов - логина и пароля. С их помощью устанавливаем объект IPrincipal. Для примера я взял
встроенный класс GenericPrincipal
, но можно сделать свой объект IPrincipal. Кроме того, я определил массив ролей с одной ролью user. Хотя
роли в данном случае не играют значение, но они требуются для создания объекта GenericPrincipal.
Если данные об авторизации не установлены и объект IPrincipal равен null, то выдаем ошибку UnauthorizedResult
. Клиенту при получении
ошибки будет отображаться окно для ввода аутентификационных данных:
Хотя тут объекту IPrincipal просто присваивается полученный из заголовков логин и случайная роль, но в реальном приложении мы бы обращались к базе данных для получения пользователя и его роли из бд. И в зависимости от результатов выборки производили те или иные действия.