Защищенный веб-API: проверка областей и ролей приложения
В этой статье описывается, как можно добавить авторизацию в веб-API. Эта защита гарантирует, что API будет вызываться только:
- приложениями от имени пользователей с нужными областями и ролями;
- управляющими приложениями, имеющими правильные роли приложений.
Фрагменты кода в этой статье взяты из следующих примеров кода на сайте GitHub:
Чтобы защитить веб-API ASP.NET или ASP.NET Core, необходимо добавить атрибут [Authorize]
в один из следующих элементов:
- сам контроллер, если требуется защитить все действия контроллера;
- действие отдельного контроллера для API.
[Authorize]
public class TodoListController : Controller
{
// ...
}
Но этой защиты недостаточно. Она гарантирует только то, что ASP.NET и ASP.NET Core проверят маркер. API необходимо проверить, что маркер, используемый для вызова API, запрашивается с ожидаемыми утверждениями. В частности, требуют проверки следующие утверждения.
- Области, если API вызывается от имени пользователя.
- Роли приложения, если API можно вызывать из управляющего приложения.
Проверка областей в интерфейсах API, вызываемых от имени пользователей
Если клиентское приложение вызывает API от имени пользователя, API должен запросить токен носителя с конкретными областями для API. Дополнительные сведения см. в Конфигурация кода | Токен носителя.
В ASP.NET Core можно использовать Microsoft.Identity.Web для проверки областей в каждом действии контроллера. Вы также можете проверять их на уровне отдельного контроллера или приложения в целом.
Проверка областей для каждого действия контроллера
Чтобы проверить области для действия контроллера, используйте атрибут [RequiredScope]
. Этот атрибут поддерживает несколько переопределений. Одно из них напрямую принимает требуемые области, а другое принимает ключ конфигурации.
Проверка областей для действия контроллера с жестко заданными областями
В следующем фрагменте кода показано использование атрибута [RequiredScope]
с жестко заданными областями.
using Microsoft.Identity.Web
[Authorize]
public class TodoListController : Controller
{
/// <summary>
/// The web API will accept only tokens that have the `access_as_user` scope for
/// this API.
/// </summary>
const string scopeRequiredByApi = "access_as_user";
// GET: api/values
[HttpGet]
[RequiredScope(scopeRequiredByApi)]
public IEnumerable<TodoItem> Get()
{
// Do the work and return the result.
// ...
}
// ...
}
Проверка областей для действия контроллера с определением областей в конфигурации
Вы также можете объявить требуемые области в конфигурации и передать ссылку на ключ этой конфигурации:
Например, если в appsettings.json у вас есть следующая конфигурация:
{
"AzureAd" : {
// more settings
"Scopes" : "access_as_user access_as_admin"
}
}
В этом случае ссылка на область в атрибуте [RequiredScope]
будет выглядеть так:
using Microsoft.Identity.Web
[Authorize]
public class TodoListController : Controller
{
// GET: api/values
[HttpGet]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
public IEnumerable<TodoItem> Get()
{
// Do the work and return the result.
// ...
}
// ...
}
Проверка областей с условием
В некоторых случаях вам нужна условная проверка областей. Для нее можно применить метод расширения VerifyUserHasAnyAcceptedScope
для HttpContext
.
using Microsoft.Identity.Web
[Authorize]
public class TodoListController : Controller
{
/// <summary>
/// The web API will accept only tokens 1) for users, 2) that have the `access_as_user` scope for
/// this API.
/// </summary>
static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };
// GET: api/values
[HttpGet]
public IEnumerable<TodoItem> Get()
{
HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
// Do the work and return the result.
// ...
}
// ...
}
Проверка областей на уровне контроллера
Вы также можете проверять области для всего контроллера.
Проверка области для контроллера с жестко заданными областями
В следующем фрагменте кода показано использование атрибута [RequiredScope]
с жестко заданными областями для контроллера. Чтобы использовать RequiredScopeAttribute, вам потребуется:
- Использование
AddMicrosoftIdentityWebApi
в Startup.cs, как показано в конфигурации кода - или в противном случае добавьте
ScopeAuthorizationRequirement
политики авторизации, как описано в политиках авторизации.
using Microsoft.Identity.Web
[Authorize]
[RequiredScope(scopeRequiredByApi)]
public class TodoListController : Controller
{
/// <summary>
/// The web API will accept only tokens 1) for users, 2) that have the `access_as_user` scope for
/// this API.
/// </summary>
static readonly string[] scopeRequiredByApi = new string[] { "access_as_user" };
// GET: api/values
[HttpGet]
public IEnumerable<TodoItem> Get()
{
// Do the work and return the result.
// ...
}
// ...
}
Проверка областей для контроллера с определением областей в конфигурации
Как и для действия, вы можете объявить требуемые области в конфигурации и передать ссылку на ключ этой конфигурации:
using Microsoft.Identity.Web
[Authorize]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
public class TodoListController : Controller
{
// GET: api/values
[HttpGet]
public IEnumerable<TodoItem> Get()
{
// Do the work and return the result.
// ...
}
// ...
}
Проверка областей на глобальном уровне
Мы рекомендуем детально определить области действия для веб-API и проверить эти области для каждого действия контроллера. Но вы можете проверить области на уровне приложения или контроллера. Дополнительные сведения см . в документации по ASP.NET Core на основе утверждений.
Что входит в проверку?
Атрибут [RequiredScope]
и метод VerifyUserHasAnyAcceptedScope
выполняют примерно следующие шаги:
- Проверьте, существует ли утверждение с именем
http://schemas.microsoft.com/identity/claims/scope
илиscp
. - Проверьте, что утверждение имеет значение, которое содержит область, ожидаемую API.
Проверка ролей приложения в интерфейсах API, которые вызываются приложениями управляющей программы
Если веб-API вызывается приложением управляющей программы, это приложение должно требовать разрешения приложения для веб-API. Как показано в разделе Предоставление разрешений приложения (роли приложений), API предоставляет такие разрешения. Одним из примеров является роль приложения access_as_application
.
Теперь необходимо, чтобы ваш API проверял, что полученный маркер содержит утверждение roles
и что это утверждение имеет ожидаемое значение. Код проверки аналогичен коду, который проверяет делегированные разрешения, за исключением того, что действие контроллера проверяет роли, а не области.
В следующем фрагменте кода показано, как проверить роль приложения;
using Microsoft.Identity.Web
[Authorize]
public class TodoListController : ApiController
{
public IEnumerable<TodoItem> Get()
{
HttpContext.ValidateAppRole("access_as_application");
// ...
}
Вместо этого можно использовать [Authorize(Roles = "access_as_application")]
атрибуты на контроллере или действии (или на странице razor).
[Authorize(Roles = "access_as_application")]
MyController : ApiController
{
// ...
}
Статья Авторизация на основе ролей в ASP.NET Core содержит несколько подходов к реализации авторизации на основе ролей. Разработчики могут выбрать один из них, который подходит для их сценариев.
Рабочие примеры см. в пошаговом руководстве по созданию веб-приложения с авторизацией по ролям и группам.
Проверка ролей приложений в интерфейсах API, вызываемых от имени пользователей
Пользователи также могут использовать утверждения ролей в шаблонах назначения пользователей, как показано в разделе Практическое руководство. Добавление ролей приложения в приложение и их получение в токене. Если роли могут быть назначены обоим, проверка ролей позволит приложениям входить в систему в качестве пользователей, а пользователям входить в качестве приложений. Для предотвращения этой путаницы рекомендуется объявлять разные роли для пользователей и приложений.
Если вы определили роли приложений с пользователем или группой, утверждение ролей также можно проверить в API вместе с областями. Логика проверки ролей приложения в этом сценарии остается такой же, как если API вызывается приложениями управляющей программы, так как нет различий в утверждении роли для пользователя или группы и приложения.
Принятие маркеров только для приложений, если веб-API должен вызываться только управляющими приложениями
Если вы хотите, чтобы только управляющие приложения вызывали веб-API, добавьте условие, согласно которому маркер является маркером только для приложения при проверке роли приложения.
string oid = ClaimsPrincipal.Current.FindFirst("oid")?.Value;
string sub = ClaimsPrincipal.Current.FindFirst("sub")?.Value;
bool isAppOnly = oid != null && sub != null && oid == sub;
Проверка обратного условия позволяет только приложениям, которые входят в систему в качестве пользователя, вызывать ваш API.
Использование авторизации на основе списков управления доступом
Вместо авторизации на основе ролей приложения можно использовать для защиты веб-API другой шаблон авторизации, основанный на списках управления доступом (ACL), который позволяет управлять маркерами без утверждения roles
.
Если вы используете ASP.NET Core, необходимо объявить, что вы используете Microsoft.Identity.Web
авторизацию на основе ACL, в противном случае Microsoft Identity Web создаст исключение, если ни роли, ни области не указаны в предоставленных утверждениях:
System.UnauthorizedAccessException: IDW10201: Neither scope or roles claim was found in the bearer token.
Чтобы избежать этого исключения, задайте AllowWebApiToBeAuthorizedByACL
для свойства конфигурации значение true
appsettings.json или программно.
{
"AzureAD"
{
// other properties
"AllowWebApiToBeAuthorizedByACL" : true,
// other properties
}
}
Если задано значение AllowWebApiToBeAuthorizedByACL
true
, это ваша ответственность за обеспечение механизма ACL.