Проверка подлинности и авторизация веб-API ASP.NET

Рик Андерсон

Вы создали веб-API, но теперь хотите управлять доступом к нему. В этой серии статей мы рассмотрим некоторые варианты защиты веб-API от неавторизованных пользователей. В этой серии рассматриваются проверка подлинности и авторизация.

  • Проверка подлинности — это знание удостоверения пользователя. Например, Алиса входит в систему с помощью имени пользователя и пароля, а сервер использует пароль для проверки подлинности Алисы.
  • Авторизация определяет, разрешено ли пользователю выполнять действие. Например, Алиса имеет разрешение на получение ресурса, но не на создание ресурса.

В первой статье серии содержится общий обзор проверки подлинности и авторизации в веб-API ASP.NET. В других разделах описаны распространенные сценарии проверки подлинности для веб-API.

Примечание

Спасибо людям, которые рассмотрели эту серию и предоставили ценные отзывы: Рик Андерсон, Леви Бродерик, Барри Дорранс, Том Дайкстра, Hongmei Ge, Дэвид Мэтсон, Даниэль Рот, Тим Teebken.

Проверка подлинности

Веб-API предполагает, что проверка подлинности выполняется на узле. Для веб-размещения используется служба IIS, которая использует http-модули для проверки подлинности. Вы можете настроить проект для использования любого из модулей проверки подлинности, встроенных в IIS или ASP.NET, или написать собственный модуль HTTP для выполнения пользовательской проверки подлинности.

Когда узел выполняет проверку подлинности пользователя, он создает субъект, который представляет собой объект IPrincipal , представляющий контекст безопасности, в котором выполняется код. Узел подключает субъект к текущему потоку, задав Thread.CurrentPrincipal. Субъект содержит связанный объект Identity , содержащий сведения о пользователе. Если пользователь прошел проверку подлинности, свойство Identity.IsAuthenticated возвращает значение true. Для анонимных запросов IsAuthenticated возвращает значение false. Дополнительные сведения о субъектах см. в разделе Безопасность на основе ролей.

Обработчики http-сообщений для проверки подлинности

Вместо использования узла для проверки подлинности можно поместить логику проверки подлинности в обработчик сообщений HTTP. В этом случае обработчик сообщений проверяет HTTP-запрос и задает субъект.

Когда следует использовать обработчики сообщений для проверки подлинности? Ниже приведены некоторые компромиссы.

  • Модуль HTTP видит все запросы, проходящие через конвейер ASP.NET. Обработчик сообщений видит только запросы, которые перенаправляются в веб-API.
  • Вы можете задать обработчики сообщений для каждого маршрута, что позволяет применять схему проверки подлинности к определенному маршруту.
  • Модули HTTP относятся к службам IIS. Обработчики сообщений не зависят от узла, поэтому их можно использовать как с веб-размещением, так и с самостоятельным размещением.
  • Модули HTTP участвуют в ведении журнала IIS, аудите и т. д.
  • Модули HTTP запускаются ранее в конвейере. При обработке проверки подлинности в обработчике сообщений субъект не будет задан до запуска обработчика. Кроме того, участник возвращается к предыдущему субъекту, когда ответ покидает обработчик сообщения.

Как правило, если вам не нужно поддерживать самостоятельное размещение, лучше всего использовать модуль 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;
    }
}

Для веб-размещения необходимо задать субъект в обоих местах; В противном случае контекст безопасности может стать несогласованным. Однако для самостоятельного размещения HttpContext.Current имеет значение NULL. Чтобы убедиться, что код не зависит от узла, проверка значение NULL перед назначением HttpContext.Current, как показано ниже.

Авторизация

Авторизация выполняется позже в конвейере, ближе к контроллеру. Это позволяет сделать более детализированный выбор при предоставлении доступа к ресурсам.

  • Фильтры авторизации выполняются перед действием контроллера. Если запрос не авторизован, фильтр возвращает ответ об ошибке, а действие не вызывается.
  • В действии контроллера текущий субъект можно получить из свойства ApiController.User . Например, можно отфильтровать список ресурсов по имени пользователя, возвращая только те ресурсы, которые принадлежат данному пользователю.

Схема конвейера проверки подлинности и авторизации.

Использование атрибута [Authorize]

Веб-API предоставляет встроенный фильтр авторизации AuthorizeAttribute. Этот фильтр проверяет, прошел ли пользователь проверку подлинности. В противном случае он возвращает код состояния HTTP 401 (Не авторизовано) без вызова действия.

Фильтр можно применять глобально, на уровне контроллера или на уровне отдельных действий.

Глобально. Чтобы ограничить доступ для каждого контроллера веб-API, добавьте фильтр 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() { ... }
}

Действие. Чтобы ограничить доступ для определенных действий, добавьте атрибут в метод action:

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 для контроллеров веб-API находится в пространстве имен System.Web.Http . В пространстве имен System.Web.Mvc существует аналогичный фильтр для контроллеров MVC, который несовместим с контроллерами веб-API.

Настраиваемые фильтры авторизации

Чтобы написать пользовательский фильтр авторизации, наследуйте от одного из следующих типов:

  • AuthorizeAttribute. Расширьте этот класс для выполнения логики авторизации на основе текущего пользователя и ролей пользователя.
  • AuthorizationFilterAttribute. Расширьте этот класс для выполнения логики синхронной авторизации, которая не обязательно основана на текущем пользователе или роли.
  • IAuthorizationFilter. Реализуйте этот интерфейс для выполнения асинхронной логики авторизации; например, если логика авторизации выполняет асинхронные операции ввода-вывода или сетевые вызовы. (Если логика авторизации привязана к ЦП, проще наследовать от AuthorizationFilterAttribute, так как в этом случае не нужно писать асинхронный метод.)

На следующей схеме показана иерархия классов для класса AuthorizeAttribute .

Схема иерархии классов для класса Authorize Attribute.

Схема иерархии классов для класса Authorize Attribute. Атрибут авторизации находится внизу со стрелкой, указывающей вверх на атрибут фильтра авторизации, и стрелкой вверх, указывающей на I Authorization Filter в верхней части.

Авторизация внутри действия контроллера

В некоторых случаях можно разрешить выполнение запроса, но изменить поведение в зависимости от субъекта. Например, возвращаемые сведения могут изменяться в зависимости от роли пользователя. В методе контроллера можно получить текущий субъект из свойства ApiController.User .

public HttpResponseMessage Get()
{
    if (User.IsInRole("Administrators"))
    {
        // ...
    }
}