Статьи

От API-ключа к пользователю с ASP.NET Web API

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.