Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье описана реализация авторизации в веб-API ASP.NET Core с помощью Microsoft. Identity.Web. Вы проверите области (делегированные разрешения) и разрешения приложения (разрешения приложения) для управления доступом к защищенным ресурсам. Примеры используют Microsoft Entra ID в качестве поставщика удостоверений.
Основные понятия авторизации
В этом разделе рассматриваются ключевые различия между аутентификацией и авторизацией, а также описывается, что Microsoft.Identity.Web проверяет в токенах доступа.
Проверка подлинности и авторизация
| Концепция | Purpose | Результат |
|---|---|---|
| Аутентификация | Проверка удостоверения | 401 Несанкционированный при сбое |
| Авторизация | Проверка разрешений | 403 Запрещено, если недостаточно |
Что проверяется
Когда веб-API получает маркер доступа, Microsoft. Identity.Web проверяет:
- Сигнатура токена — это из доверенного центра?
- Аудитория токенов — предназначена ли она для этого API?
- Срок действия маркера — является ли он по-прежнему действительным?
- Области и роли — имеет ли клиентское приложение и субъект (пользователь) правильные разрешения?
В этом руководстве основное внимание уделяется проверке областей и разрешений приложений.
Области (делегированные разрешения)
Области действия применяются, когда пользователь делегирует разрешение приложению действовать от своего имени (например, веб-API, вызываемому от имени вошедшего пользователя).
| Detail | Значение |
|---|---|
| Утверждение токена |
scp или scope (клиентское приложение); roles (пользователь) |
| Примеры значений |
"access_as_user"
"User.Read"
"Files.ReadWrite"
|
Разрешения приложения
Разрешения приложения применяются при вызове веб-API как самого себя без контекста пользователя, например управляющей программы или фоновой службы с использованием учетных данных клиента.
| Detail | Значение |
|---|---|
| Утверждение токена | roles |
| Примеры значений |
"Mail.Read.All", "User.Read.All" |
Проверка областей с помощью RequiredScope
Атрибут RequiredScope проверяет, содержит ли маркер доступа по крайней мере одну из указанных областей. Используйте этот атрибут, если API обслуживает только делегированные пользователем запросы.
Настройка проверки области
Выполните следующие действия, чтобы включить проверку области в API.
1. Включите авторизацию в API:
Добавьте службы проверки подлинности и авторизации в конвейер приложения:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization(); // Required for authorization
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization(); // Must be after UseAuthentication
app.MapControllers();
app.Run();
2. Защита контроллеров или действий:
Примените атрибуты [Authorize] и [RequiredScope] к вашему контроллеру или отдельным действиям.
using Microsoft.AspNetCore.Authorization;
using Microsoft.Identity.Web.Resource;
[Authorize]
[RequiredScope("access_as_user")]
public class TodoListController : ControllerBase
{
[HttpGet]
public IActionResult GetTodos()
{
// Only accessible if token has "access_as_user" scope
return Ok(new[] { "Todo 1", "Todo 2" });
}
}
Применение шаблонов областей
Выберите шаблон, который лучше всего подходит для управления областями в приложении.
Шаблон 1. Жестко закодированные области
Используйте этот шаблон, когда границы фиксированы и известны во время разработки.
[Authorize]
[RequiredScope("access_as_user")]
public class TodoListController : ControllerBase
{
// All actions require "access_as_user" scope
}
Чтобы принять одну из нескольких областей, выведите их в качестве параметров:
[Authorize]
[RequiredScope("read", "write", "admin")]
public class TodoListController : ControllerBase
{
// Token must have "read" OR "write" OR "admin"
}
Шаблон 2. Области видимости из конфигурации
Используйте этот шаблон, если области должны быть настраиваемыми для каждой среды. Определите области в файле конфигурации:
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-api-client-id",
"Scopes": "access_as_user read write"
}
}
Ссылка на ключ конфигурации в контроллере:
[Authorize]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
public class TodoListController : ControllerBase
{
// Scopes read from configuration
}
Этот подход позволяет изменять области без перекомпиляции.
Шаблон 3. Области уровня действия
Используйте этот шаблон, если для различных действий требуются разные разрешения. Применение [RequiredScope] к отдельным методам действия:
[Authorize]
public class TodoListController : ControllerBase
{
[HttpGet]
[RequiredScope("read")]
public IActionResult GetTodos()
{
return Ok(todos);
}
[HttpPost]
[RequiredScope("write")]
public IActionResult CreateTodo([FromBody] Todo todo)
{
// Only tokens with "write" scope can create
return CreatedAtAction(nameof(GetTodos), todo);
}
[HttpDelete("{id}")]
[RequiredScope("admin")]
public IActionResult DeleteTodo(int id)
{
// Only tokens with "admin" scope can delete
return NoContent();
}
}
Общие сведения о потоке проверки
При поступлении запроса ПО промежуточного слоя обрабатывает его в следующем порядке:
- Промежуточное программное обеспечение для аутентификации ASP.NET Core проверяет токен.
-
RequiredScopeпроверяет атрибут для утвержденияscpилиscope - Если маркер содержит по крайней мере одну соответствующую область, запрос выполняется.
- Если область сопоставления не найдена, API возвращает ответ 403 Запрещено.
В следующем примере показан типичный ответ на ошибку:
{
"error": "insufficient_scope",
"error_description": "The token does not have the required scope 'access_as_user'."
}
Проверка разрешений приложения с помощью RequiredScopeOrAppPermission
Атрибут RequiredScopeOrAppPermission проверяет области (делегированные) или разрешения приложения (приложение). Используйте этот атрибут, если API обслуживает как приложения, делегированные пользователем, так и приложения управляющей программы или службы из одной конечной точки.
Если API обслуживает только делегированные пользователем запросы, используйте RequiredScope вместо этого.
Настройка области действия или проверки разрешений приложения
Примените атрибут, чтобы принимать один из типов токенов.
using Microsoft.Identity.Web.Resource;
[Authorize]
[RequiredScopeOrAppPermission(
AcceptedScope = new[] { "access_as_user" },
AcceptedAppPermission = new[] { "TodoList.ReadWrite.All" }
)]
public class TodoListController : ControllerBase
{
[HttpGet]
public IActionResult GetTodos()
{
// Accessible with EITHER:
// - User-delegated token with "access_as_user" scope, OR
// - App-only token with "TodoList.ReadWrite.All" app permission
return Ok(todos);
}
}
Настройка разрешений приложения из параметров
Храните области и разрешения приложений в конфигурации, чтобы изменить их без повторной компиляции.
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "your-tenant-id",
"ClientId": "your-api-client-id",
"Scopes": "access_as_user",
"AppPermissions": "TodoList.ReadWrite.All TodoList.Admin"
}
}
Ссылка на ключи конфигурации в контроллере:
[Authorize]
[RequiredScopeOrAppPermission(
RequiredScopesConfigurationKey = "AzureAd:Scopes",
RequiredAppPermissionsConfigurationKey = "AzureAd:AppPermissions"
)]
public class TodoListController : ControllerBase
{
// Scopes and app permissions from configuration
}
Сравнение различий утверждений токена
В следующей таблице показано, как претензии отличаются между делегированными пользователем и только приложениями токенами.
| Тип маркера | Утверждение | Пример значения |
|---|---|---|
| Делегированный пользователем |
scp или scope |
"access_as_user User.Read" |
| Только для приложений | roles |
["TodoList.ReadWrite.All"] |
В следующем примере показан маркер, делегированный пользователем:
{
"aud": "api://your-api-client-id",
"iss": "https://login.microsoftonline.com/.../v2.0",
"scp": "access_as_user",
"sub": "user-object-id",
...
}
В следующем примере показан маркер только для приложений:
{
"aud": "api://your-api-client-id",
"iss": "https://login.microsoftonline.com/.../v2.0",
"roles": ["TodoList.ReadWrite.All"],
"sub": "app-object-id",
...
}
Создание политик авторизации
Для сложных сценариев авторизации используйте политики авторизации ASP.NET Core. Политики позволяют централизировать правила, объединять несколько требований и записывать тестируемую логику авторизации.
| Преимущества | Description |
|---|---|
| Централизованная логика | Определение правил авторизации один раз, повторное использование везде |
| Компонуемый | Объединить несколько требований (области доступа, утверждения и настраиваемую логику) |
| Тестируемый | Упрощение модульного тестирования логики авторизации |
| Гибкий | Пользовательские требования за пределами проверки области |
Шаблон 1. Определение политики с помощью RequireScope
Определите именованные политики, требующие определенных областей, а затем ссылайтесь на них на контроллерах:
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("TodoReadPolicy", policyBuilder =>
{
policyBuilder.RequireScope("read", "access_as_user");
});
options.AddPolicy("TodoWritePolicy", policyBuilder =>
{
policyBuilder.RequireScope("write", "admin");
});
});
var app = builder.Build();
Применение политик к действиям контроллера:
[Authorize]
public class TodoListController : ControllerBase
{
[HttpGet]
[Authorize(Policy = "TodoReadPolicy")]
public IActionResult GetTodos()
{
return Ok(todos);
}
[HttpPost]
[Authorize(Policy = "TodoWritePolicy")]
public IActionResult CreateTodo([FromBody] Todo todo)
{
return CreatedAtAction(nameof(GetTodos), todo);
}
}
Шаблон 2. Определение политики с помощью ScopeAuthorizationRequirement
Используйте ScopeAuthorizationRequirement для более явных требований к области:
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.Resource;
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("CustomPolicy", policyBuilder =>
{
policyBuilder.AddRequirements(
new ScopeAuthorizationRequirement(new[] { "access_as_user" })
);
});
});
Шаблон 3. Настройка политики по умолчанию
Задайте политику по умолчанию, которая применяется ко всем [Authorize] атрибутам автоматически:
builder.Services.AddAuthorization(options =>
{
var defaultPolicy = new AuthorizationPolicyBuilder()
.RequireScope("access_as_user")
.Build();
options.DefaultPolicy = defaultPolicy;
});
Теперь для каждого [Authorize] атрибута требуется область access_as_user.
[Authorize] // Automatically requires "access_as_user" scope
public class TodoListController : ControllerBase
{
// All actions protected by default policy
}
Шаблон 4. Объединение нескольких требований
Объедините требования к области, роли и аутентификации в одной политике:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminPolicy", policyBuilder =>
{
policyBuilder.RequireScope("admin");
policyBuilder.RequireRole("Admin"); // Also check role claim
policyBuilder.RequireAuthenticatedUser();
});
});
Шаблон 5. Создание политики из конфигурации
Загрузка областей из конфигурации для поддержания специфики сред для политик.
var requiredScopes = builder.Configuration["AzureAd:Scopes"]?.Split(' ');
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ApiAccessPolicy", policyBuilder =>
{
if (requiredScopes != null)
{
policyBuilder.RequireScope(requiredScopes);
}
});
});
Фильтрация запросов по клиенту
Ограничьте доступ API к токенам из определенных арендаторов Microsoft Entra. Это полезно, если API с несколькими клиентами должен принимать только запросы от утвержденных клиентов.
Ограничить доступ разрешенным арендаторам
Определите политику, которая проверяет утверждение идентификатора клиента в списке разрешений:
builder.Services.AddAuthorization(options =>
{
string[] allowedTenants =
{
"14c2f153-90a7-4689-9db7-9543bf084dad", // Contoso tenant
"af8cc1a0-d2aa-4ca7-b829-00d361edb652", // Fabrikam tenant
"979f4440-75dc-4664-b2e1-2cafa0ac67d1" // Northwind tenant
};
options.AddPolicy("AllowedTenantsOnly", policyBuilder =>
{
policyBuilder.RequireClaim(
"http://schemas.microsoft.com/identity/claims/tenantid",
allowedTenants
);
});
// Apply to all endpoints by default
options.DefaultPolicy = options.GetPolicy("AllowedTenantsOnly");
});
Настройка фильтрации арендаторов в настройках
Храните идентификаторы арендаторов в конфигурации для управления ими без изменений кода.
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "your-api-client-id",
"AllowedTenants": [
"14c2f153-90a7-4689-9db7-9543bf084dad",
"af8cc1a0-d2aa-4ca7-b829-00d361edb652"
]
}
}
Прочитайте список клиентов и создайте политику при запуске:
var allowedTenants = builder.Configuration.GetSection("AzureAd:AllowedTenants")
.Get<string[]>();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AllowedTenantsOnly", policyBuilder =>
{
policyBuilder.RequireClaim(
"http://schemas.microsoft.com/identity/claims/tenantid",
allowedTenants ?? Array.Empty<string>()
);
});
});
Объединение областей с фильтрацией клиента
Создайте политику, требующую допустимой области и утвержденного клиента:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("SecureApiAccess", policyBuilder =>
{
// Require specific scope
policyBuilder.RequireScope("access_as_user");
// AND require specific tenant
policyBuilder.RequireClaim(
"http://schemas.microsoft.com/identity/claims/tenantid",
allowedTenants
);
});
});
Следуйте лучшим практикам
Примените эти рекомендации для создания безопасной, поддерживаемой логики авторизации.
Что нужно делать
1. Всегда сочетайте [Authorize] с валидацией области:
[Authorize] // Authentication
[RequiredScope("access_as_user")] // Authorization
public class MyController : ControllerBase { }
2. Используйте конфигурацию для областей, зависящих от среды:
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
3. Применение наименьших привилегий:
[HttpGet]
[RequiredScope("read")] // Only read permission needed
[HttpPost]
[RequiredScope("write")] // Write permission for modifications
4. Используйте политики для сложной авторизации:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy =>
{
policy.RequireScope("admin");
policy.RequireClaim("department", "IT");
});
});
5. Включите подробные ответы на ошибки в разработке:
if (builder.Environment.IsDevelopment())
{
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
}
Чего не следует делать
1. Не пропускайте [Authorize] при использовании RequiredScope:
// Wrong - RequiredScope won't work without [Authorize]
[RequiredScope("access_as_user")]
public class MyController : ControllerBase { }
// Correct
[Authorize]
[RequiredScope("access_as_user")]
public class MyController : ControllerBase { }
2. Не жёстко кодируйте идентификаторы арендатора в продакшене:
// Wrong
policyBuilder.RequireClaim("tid", "14c2f153-90a7-4689-9db7-9543bf084dad");
// Better - use configuration
var tenants = Configuration.GetSection("AllowedTenants").Get<string[]>();
policyBuilder.RequireClaim("tid", tenants);
3. Не путайте области с ролями:
// Wrong - This checks roles claim, not scopes
[RequiredScope("Admin")] // "Admin" is typically a role, not a scope
// Correct
[RequiredScope("access_as_user")] // Scope
[Authorize(Roles = "Admin")] // Role
4. Не предоставляйте конфиденциальные сведения о области в сообщениях об ошибках в производственной среде:
Настройте соответствующие уровни ведения журнала и обработку ошибок для рабочих сред.
Устранение неполадок авторизации
Используйте следующее руководство для диагностики распространенных проблем авторизации.
403 Запрещено — недостающая область действия
Ошибка: API возвращает 403 даже при наличии корректного токена.
Диагноз:
- Декодируйте токен на jwt.ms.
- Проверьте
scpилиscopeзаявление. - Убедитесь, что значение соответствует атрибуту
RequiredScope.
Solution:
- Убедитесь, что клиентское приложение запрашивает правильный scope при получении токена.
- Убедитесь, что область видимости указана в регистрации приложения API в Microsoft Entra.
- При необходимости предоставьте согласие администратора.
RequiredScope не работает
Симптом: Атрибут, как представляется, игнорируется.
Проверьте:
- Вы добавили
[Authorize]атрибут? - Вызывается
app.UseAuthorization()послеapp.UseAuthentication()? - Зарегистрировано ли
services.AddAuthorization()?
Ключ конфигурации не найден
Ошибка: Проверка области завершается без уведомления.
Проверьте:
{
"AzureAd": {
"Scopes": "access_as_user" // Matches RequiredScopesConfigurationKey
}
}
Убедитесь, что путь конфигурации совпадает точно.