ASP.NET Web API — отличный инструмент для создания API. Или, как всегда говорит мой приятель Кристоф Реннен (и французы): «это делает тебя апи». Одна из вещей, которые мне очень нравятся, это то, что вы можете делать очень мощные вещи, которые вы знаете и любите из стека ASP.NET MVC, например, используя атрибуты фильтра. Фильтры действий, фильтры результатов и … фильтры авторизации.
Скажем, вы хотели защитить свой API и использовать свойство User контроллера для возврата информации, специфичной для пользователя. Вы, вероятно, добавите атрибут [Authorize] (чтобы гарантировать аутентификацию пользователя) либо на весь контроллер API, либо на один из его методов действия, например так:
[Authorize] public class SuperSecretController : ApiController { public string Get() { return string.Format("Hello, {0}", User.Identity.Name); } }
Большой! Но как ваше приложение узнает, кто звонит? Проверка подлинности с помощью форм не имеет смысла для многих API. Настройка IIS и переключение на проверку подлинности Windows или обычную проверку подлинности может быть одним из вариантов. Но не каждый ASP.NET Web API будет жить в IIS, верно? А может быть, вы хотите использовать какую-то другую форму аутентификации для вашего API, например, такую, которая использует собственный заголовок HTTP, содержащий ключ API? Давайте посмотрим, как вы можете сделать это …
Наш API аутентификация? Ключ API
Ключи API могут иметь смысл для вашего API. Они предоставляют простой способ аутентификации ваших пользователей API на основе простого токена, который передается в пользовательском заголовке. OAuth2 также может иметь смысл, но даже он сводится к пользовательскому заголовку авторизации на уровне HTTP. (подсказка: подход, описанный в этом посте, можно использовать и для токенов OAuth2)
Давайте создадим наш API и потребуем, чтобы каждый потребитель API передавал пользовательский заголовок с именем «X-ApiKey». Звонки в наш API будут выглядеть так:
GET http://localhost:60573/api/v1/SuperSecret HTTP/1.1 Host: localhost:60573 X-ApiKey: 12345
В нашем SuperSecretController, приведенном выше, мы хотим убедиться, что мы работаем с традиционным IPrincipal, который может запрашивать имя пользователя, роли и, возможно, даже утверждения, если это необходимо. Как мы можем получить эту личность там?
Перевод ключа API с использованием DelegatingHandler
Название уже дает вам указатель. Мы хотим добавить плагин в конвейер ASP.NET Web API, который заменяет IPrincipal текущего потока на тот, который отображается из входящего ключа API. Этот плагин будет представлен в форме DelegatingHandler , класса, который был подключен очень рано в конвейере ASP.NET Web API. Я не буду вдаваться в подробности о том , что DelegatingHandler делает и где он подходит, там идеальный пост на том , что можно найти здесь .
Наш обработчик, который я назову AuthorizationHeaderHandler, будет наследовать DelegatingHandler веб-API ASP.NET . Нас интересует метод SendAsync , который будет вызываться при каждом запросе в нашем API.
public class AuthorizationHeaderHandler : DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { // ... } }
Этот метод предлагает доступ к HttpRequestMessage , который содержит все, что вам, вероятно, понадобится, например … HTTP-заголовки! Давайте прочитаем наш заголовок X-ApiKey , преобразуем его в ClaimsIdentity (чтобы мы могли добавить дополнительные утверждения при необходимости) и назначим его текущему потоку:
public class AuthorizationHeaderHandler : DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { IEnumerable<string> apiKeyHeaderValues = null; if (request.Headers.TryGetValues("X-ApiKey", out apiKeyHeaderValues)) { var apiKeyHeaderValue = apiKeyHeaderValues.First(); // ... your authentication logic here ... var username = (apiKeyHeaderValue == "12345" ? "Maarten" : "OtherUser"); var usernameClaim = new Claim(ClaimTypes.Name, username); var identity = new ClaimsIdentity(new[] {usernameClaim}, "ApiKey"); var principal = new ClaimsPrincipal(identity); Thread.CurrentPrincipal = principal; } return base.SendAsync(request, cancellationToken); } }
Легко нет? Осталось только зарегистрировать этот обработчик в конвейере во время запуска вашего приложения:
GlobalConfiguration.Configuration.MessageHandlers.Add(new AuthorizationHeaderHandler());
Отныне любой запрос, поступающий с заголовком X-ApiKey, будет преобразован в IPrincipal, который вы можете легко использовать в своем веб-API. Наслаждайтесь!
PS: если вы изучаете OAuth2, я использовал аналогичный подход в « Делегировании OAuth2 веб-API ASP.NET с Windows Azure Access Control Service » для обработки маркеров OAuth2.