Руководство. Защита веб-API ASP.NET Core, зарегистрированного во внешнем клиенте
В этой серии руководств показано, как защитить зарегистрированный веб-API во внешнем клиенте. В этом руководстве вы создадите веб-API ASP.NET Core, который публикует делегированные разрешения (области) и разрешения приложения (роли приложения).
В этом руководстве;
- Настройка веб-API для использования сведений о регистрации приложения
- Настройка веб-API для использования делегированных и разрешений приложений, зарегистрированных в регистрации приложения
- Защита конечных точек веб-API
Необходимые компоненты
Регистрация API, которая предоставляет по крайней мере одну область (делегированные разрешения) и одну роль приложения (разрешение приложения), например ToDoList.Read. Если вы еще не сделали этого, зарегистрируйте API в Центре администрирования Microsoft Entra, выполнив действия по регистрации. Убедитесь, что у вас есть следующее:
- Идентификатор приложения (клиента) веб-API
- Зарегистрирован идентификатор каталога (клиента) веб-API
- Поддомен каталога (клиента) о том, где зарегистрирован веб-API. Например, если основной домен contoso.onmicrosoft.com, поддомен каталога (клиента) — contoso.
- ToDoList.Read и ToDoList.ReadWrite в качестве делегированных разрешений (областей), предоставляемых веб-API.
- ToDoList.Read.All и ToDoList.ReadWrite.All как разрешения приложения (роли приложения), предоставляемые веб-API.
Пакет SDK для .NET 7.0 или более поздней версии.
Visual Studio Code или любой другой редактор кода.
Создание веб-API ASP.NET Core
Откройте терминал, а затем перейдите в папку, в которой вы хотите жить проект.
Выполните следующие команды:
dotnet new webapi -o ToDoListAPI cd ToDoListAPI
При появлении диалогового окна с запросом на добавление в проект необходимых ресурсов выберите Да.
Установка пакетов
Установите следующие пакеты:
Microsoft.EntityFrameworkCore.InMemory
с помощью Entity Framework Core можно использовать базу данных в памяти. Он не предназначен для использования в рабочей среде.Microsoft.Identity.Web
упрощает добавление поддержки проверки подлинности и авторизации для веб-приложений и веб-API, интегрирующихся с платформа удостоверений Майкрософт.
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.Identity.Web
Настройка сведений о регистрации приложения
Откройте файл appsettings.json в папке приложения и добавьте сведения о регистрации приложения, записанные после регистрации веб-API.
{
"AzureAd": {
"Instance": "https://Enter_the_Tenant_Subdomain_Here.ciamlogin.com/",
"TenantId": "Enter_the_Tenant_Id_Here",
"ClientId": "Enter_the_Application_Id_Here",
},
"Logging": {...},
"AllowedHosts": "*"
}
Замените следующие заполнители, как показано ниже:
- Замените
Enter_the_Application_Id_Here
идентификатором приложения (клиента). - Замените
Enter_the_Tenant_Id_Here
идентификатором каталога (клиента). - Замените
Enter_the_Tenant_Subdomain_Here
поддомен каталога (клиента).
Использование личного домена URL-адреса (необязательно)
Используйте личный домен для полной фирменной символики URL-адреса проверки подлинности. С точки зрения пользователя пользователи остаются в домене во время проверки подлинности, а не перенаправляются на ciamlogin.com доменное имя.
Выполните следующие действия, чтобы использовать личный домен:
Выполните действия, описанные в разделе "Включение пользовательских доменов URL-адресов" для приложений во внешних клиентах , чтобы включить личный ДОМЕН URL-адресов для внешнего клиента.
Откройте файл appsettings.json :
- Измените значение
Instance
свойства https://Enter_the_Custom_Domain_Here/Enter_the_Tenant_ID_Hereна . ЗаменитеEnter_the_Custom_Domain_Here
домен личного URL-адреса иEnter_the_Tenant_ID_Here
идентификатором клиента. Если у вас нет идентификатора клиента, узнайте, как прочитать сведения о клиенте. - Добавьте
knownAuthorities
свойство со значением [Enter_the_Custom_Domain_Here].
- Измените значение
После внесения изменений в файл appsettings.json, если личный домен URL-адреса login.contoso.com, а идентификатор клиента — aaaabbbb-0000-cccc-1111-dd222eeee, файл должен выглядеть следующим фрагментом кода:
{
"AzureAd": {
"Instance": "https://login.contoso.com/aaaabbbb-0000-cccc-1111-dddd2222eeee",
"TenantId": "Enter_the_Tenant_Id_Here",
"ClientId": "Enter_the_Application_Id_Here",
"KnownAuthorities": ["login.contoso.com"]
},
"Logging": {...},
"AllowedHosts": "*"
}
Добавление роли и области приложения
Все API-интерфейсы должны публиковать как минимум одну область, которая также называется делегированным разрешением, чтобы клиентские приложения получили маркер доступа для пользователя успешно. API-интерфейсы также должны публиковать как минимум одну роль приложения для приложений, также называемую разрешением приложения, чтобы клиентские приложения получили маркер доступа как сами по себе, то есть если они не входить в систему пользователя.
Мы указываем эти разрешения в файле appsettings.json . В этом руководстве мы зарегистрировали четыре разрешения. ToDoList.ReadWrite и ToDoList.Read в качестве делегированных разрешений и ToDoList.ReadWrite.All и ToDoList.Read.All в качестве разрешений приложения.
{
"AzureAd": {
"Instance": "https://Enter_the_Tenant_Subdomain_Here.ciamlogin.com/",
"TenantId": "Enter_the_Tenant_Id_Here",
"ClientId": "Enter_the_Application_Id_Here",
"Scopes": {
"Read": ["ToDoList.Read", "ToDoList.ReadWrite"],
"Write": ["ToDoList.ReadWrite"]
},
"AppPermissions": {
"Read": ["ToDoList.Read.All", "ToDoList.ReadWrite.All"],
"Write": ["ToDoList.ReadWrite.All"]
}
},
"Logging": {...},
"AllowedHosts": "*"
}
Добавление схемы проверки подлинности
Схема проверки подлинности называется при настройке службы проверки подлинности во время проверки подлинности. В этой статье мы используем схему проверки подлинности носителя JWT. Добавьте следующий код в файл Programs.cs , чтобы добавить схему проверки подлинности.
// Add the following to your imports
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
// Add authentication scheme
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration);
Создание моделей
Создайте папку с именем Models в корневой папке проекта. Перейдите в папку и создайте файл с именем ToDo.cs затем добавьте следующий код. Этот код создает модель с именем ToDo.
using System;
namespace ToDoListAPI.Models;
public class ToDo
{
public int Id { get; set; }
public Guid Owner { get; set; }
public string Description { get; set; } = string.Empty;
}
Добавление контекста базы данных
Основной класс Контекст базы данных координирует функциональные возможности Entity Framework для модели данных. Этот класс создается путем извлечения из класса Microsoft.EntityFrameworkCore.DbContext . В этом руководстве мы используем базу данных в памяти для тестирования.
Создайте папку с именем DbContext в корневой папке проекта.
Перейдите к этой папке и создайте файл с именем ToDoContext.cs затем добавьте в него следующее содержимое:
using Microsoft.EntityFrameworkCore; using ToDoListAPI.Models; namespace ToDoListAPI.Context; public class ToDoContext : DbContext { public ToDoContext(DbContextOptions<ToDoContext> options) : base(options) { } public DbSet<ToDo> ToDos { get; set; } }
Откройте файл Program.cs в корневой папке приложения, а затем добавьте следующий код в файл. Этот код регистрирует
DbContext
подкласс, называемыйToDoContext
службой с областью действия в поставщике служб приложений ASP.NET Core (также называемом контейнером внедрения зависимостей). Контекст настроен для использования базы данных в памяти.// Add the following to your imports using ToDoListAPI.Context; using Microsoft.EntityFrameworkCore; builder.Services.AddDbContext<ToDoContext>(opt => opt.UseInMemoryDatabase("ToDos"));
Добавление контроллеров
В большинстве случаев контроллер будет иметь несколько действий. Обычно действия создания, чтения, обновления и удаления (CRUD). В этом руководстве мы создадим только два элемента действия. Прочтите все элементы действия и элемент создания действия, чтобы продемонстрировать, как защитить конечные точки.
Перейдите в папку Controllers в корневой папке проекта.
Создайте файл с именем ToDoListController.cs в этой папке. Откройте файл, а затем добавьте следующий код плиты:
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Identity.Web; using Microsoft.Identity.Web.Resource; using ToDoListAPI.Models; using ToDoListAPI.Context; namespace ToDoListAPI.Controllers; [Authorize] [Route("api/[controller]")] [ApiController] public class ToDoListController : ControllerBase { private readonly ToDoContext _toDoContext; public ToDoListController(ToDoContext toDoContext) { _toDoContext = toDoContext; } [HttpGet()] [RequiredScopeOrAppPermission()] public async Task<IActionResult> GetAsync(){...} [HttpPost] [RequiredScopeOrAppPermission()] public async Task<IActionResult> PostAsync([FromBody] ToDo toDo){...} private bool RequestCanAccessToDo(Guid userId){...} private Guid GetUserId(){...} private bool IsAppMakingRequest(){...} }
Добавление кода в контроллер
В этом разделе мы добавим код в созданные заполнители. Основное внимание здесь не уделяется созданию API, а скорее защите.
Импортируйте необходимые пакеты. Пакет Microsoft.Identity.Web — это оболочка MSAL, которая помогает нам легко обрабатывать логику проверки подлинности, например путем обработки проверки маркеров. Чтобы гарантировать, что для наших конечных точек требуется авторизация, мы используем встроенный пакет Microsoft.AspNetCore.Authorization .
Так как мы предоставили разрешения для вызова этого API с помощью делегированных разрешений от имени пользователя или приложения, где клиент вызывает себя, а не от имени пользователя, важно знать, выполняется ли вызов приложением от собственного имени. Самый простой способ сделать это — утверждения, чтобы определить, содержит
idtyp
ли маркер доступа необязательный утверждение. Этоidtyp
утверждение является самым простым способом для API определить, является ли маркер приложения маркером или приложением + маркер пользователя. Рекомендуется включить необязательноеidtyp
утверждение.idtyp
Если утверждение не включено, можно использоватьroles
иscp
утверждения, чтобы определить, является ли маркер доступа маркером приложения или приложением + маркером пользователя. Маркер доступа, выданный Внешняя идентификация Microsoft Entra, имеет по крайней мере одно из двух утверждений. Маркеры доступа, выданныеscp
пользователю, имеют утверждение. Маркеры доступа, выданные приложениюroles
, имеют утверждение. Маркеры доступа, содержащие оба утверждения, выдаются только пользователям, гдеscp
утверждение назначает делегированные разрешения, аroles
утверждение обозначает роль пользователя. Маркеры доступа, которые не имеют права на соблюдение.private bool IsAppMakingRequest() { if (HttpContext.User.Claims.Any(c => c.Type == "idtyp")) { return HttpContext.User.Claims.Any(c => c.Type == "idtyp" && c.Value == "app"); } else { return HttpContext.User.Claims.Any(c => c.Type == "roles") && !HttpContext.User.Claims.Any(c => c.Type == "scp"); } }
Добавьте вспомогающую функцию, которая определяет, содержит ли запрос достаточно разрешений для выполнения предполагаемого действия. Проверьте, выполняет ли приложение запрос от собственного имени или выполняет ли приложение вызов от имени пользователя, который владеет заданным ресурсом, проверяя идентификатор пользователя.
private bool RequestCanAccessToDo(Guid userId) { return IsAppMakingRequest() || (userId == GetUserId()); } private Guid GetUserId() { Guid userId; if (!Guid.TryParse(HttpContext.User.GetObjectId(), out userId)) { throw new Exception("User ID is not valid."); } return userId; }
Подключите определения разрешений для защиты маршрутов. Защитите API, добавив
[Authorize]
атрибут в класс контроллера. Это гарантирует, что действия контроллера можно вызывать только в том случае, если API вызывается с авторизованным удостоверением. Определения разрешений определяют, какие типы разрешений необходимы для выполнения этих действий.[Authorize] [Route("api/[controller]")] [ApiController] public class ToDoListController: ControllerBase{...}
Добавьте разрешения на получение всех конечных точек и конечной точки POST. Для этого используется метод RequiredScopeOrAppPermission , который входит в пространство имен Microsoft.Identity.Web.Resource . Затем вы передаете области и разрешения этому методу с помощью атрибутов RequiredScopesConfigurationKey и RequiredAppPermissionsConfigurationKey .
[HttpGet] [RequiredScopeOrAppPermission( RequiredScopesConfigurationKey = "AzureAD:Scopes:Read", RequiredAppPermissionsConfigurationKey = "AzureAD:AppPermissions:Read" )] public async Task<IActionResult> GetAsync() { var toDos = await _toDoContext.ToDos! .Where(td => RequestCanAccessToDo(td.Owner)) .ToListAsync(); return Ok(toDos); } [HttpPost] [RequiredScopeOrAppPermission( RequiredScopesConfigurationKey = "AzureAD:Scopes:Write", RequiredAppPermissionsConfigurationKey = "AzureAD:AppPermissions:Write" )] public async Task<IActionResult> PostAsync([FromBody] ToDo toDo) { // Only let applications with global to-do access set the user ID or to-do's var ownerIdOfTodo = IsAppMakingRequest() ? toDo.Owner : GetUserId(); var newToDo = new ToDo() { Owner = ownerIdOfTodo, Description = toDo.Description }; await _toDoContext.ToDos!.AddAsync(newToDo); await _toDoContext.SaveChangesAsync(); return Created($"/todo/{newToDo!.Id}", newToDo); }
Запуск API
Запустите API, чтобы убедиться, что он работает хорошо без ошибок с помощью команды dotnet run
. Если вы планируете использовать протокол HTTPS даже во время тестирования, необходимо доверять. Сертификат разработки NET.
Полный пример этого кода API см. в файле примеров.