Samouczek: zabezpieczanie internetowego interfejsu API platformy ASP.NET Core zarejestrowanego w dzierżawie zewnętrznej
W tej serii samouczków pokazano, jak zabezpieczyć zarejestrowany internetowy interfejs API w dzierżawie zewnętrznej. W tym samouczku utworzysz internetowy interfejs API platformy ASP.NET Core, który publikuje uprawnienia delegowane (zakresy) i uprawnienia aplikacji (role aplikacji).
W tym samouczku;
- Konfigurowanie internetowego interfejsu API do używania szczegółów rejestracji aplikacji
- Konfigurowanie internetowego interfejsu API do używania uprawnień delegowanych i aplikacji zarejestrowanych w rejestracji aplikacji
- Ochrona punktów końcowych internetowego interfejsu API
Wymagania wstępne
Rejestracja interfejsu API, która uwidacznia co najmniej jeden zakres (uprawnienia delegowane) i jedną rolę aplikacji (uprawnienie aplikacji), taką jak ToDoList.Read. Jeśli jeszcze tego nie zrobiono, zarejestruj interfejs API w centrum administracyjnym firmy Microsoft Entra, wykonując kroki rejestracji. Upewnij się, że masz następujące elementy:
- Identyfikator aplikacji (klienta) internetowego interfejsu API
- Identyfikator katalogu (dzierżawy) internetowego interfejsu API jest zarejestrowany
- Poddomena katalogu (dzierżawy) lokalizacji, w której zarejestrowano internetowy interfejs API. Jeśli na przykład domena podstawowa jest contoso.onmicrosoft.com, domena podrzędna katalogu (dzierżawy) to contoso.
- ToDoList.Read i ToDoList.ReadWrite jako delegowane uprawnienia (zakresy) uwidocznione przez internetowy interfejs API.
- ToDoList.Read.All i ToDoList.ReadWrite.All jako uprawnienia aplikacji (role aplikacji) uwidocznione przez internetowy interfejs API.
Zestaw .NET 7.0 SDK lub nowszy.
Visual Studio Code lub inny edytor kodu.
Tworzenie internetowego interfejsu API platformy ASP.NET Core
Otwórz terminal, a następnie przejdź do folderu, w którym ma działać projekt.
Uruchom następujące polecenia:
dotnet new webapi -o ToDoListAPI cd ToDoListAPI
Gdy zostanie wyświetlone okno dialogowe z pytaniem, czy chcesz dodać wymagane zasoby do projektu, wybierz pozycję Tak.
Instalowanie pakietów
Zainstaluj następujące pakiety:
Microsoft.EntityFrameworkCore.InMemory
umożliwia korzystanie z programu Entity Framework Core z bazą danych w pamięci. Nie jest przeznaczony do użytku produkcyjnego.Microsoft.Identity.Web
Upraszcza dodawanie obsługi uwierzytelniania i autoryzacji do aplikacji internetowych i internetowych interfejsów API integrujących się z Platforma tożsamości Microsoft.
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.Identity.Web
Konfigurowanie szczegółów rejestracji aplikacji
Otwórz plik appsettings.json w folderze aplikacji i dodaj szczegóły rejestracji aplikacji zarejestrowane po zarejestrowaniu internetowego interfejsu 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": "*"
}
Zastąp następujące symbole zastępcze, jak pokazano:
- Zastąp
Enter_the_Application_Id_Here
element identyfikatorem aplikacji (klienta). - Zastąp
Enter_the_Tenant_Id_Here
ciąg identyfikatorem katalogu (dzierżawy). - Zastąp
Enter_the_Tenant_Subdomain_Here
ciąg domeną podrzędną Katalog (dzierżawa).
Użyj niestandardowej domeny adresu URL (opcjonalnie)
Użyj domeny niestandardowej, aby w pełni oznaczyć adres URL uwierzytelniania. Z perspektywy użytkownika użytkownicy pozostają w domenie podczas procesu uwierzytelniania, a nie przekierowywani do ciamlogin.com nazwy domeny.
Wykonaj następujące kroki, aby użyć domeny niestandardowej:
Wykonaj kroki opisane w temacie Włączanie niestandardowych domen url dla aplikacji w dzierżawach zewnętrznych, aby włączyć niestandardową domenę adresu URL dla dzierżawy zewnętrznej.
Otwórz plik appsettings.json :
- Zaktualizuj wartość
Instance
właściwości na https://Enter_the_Custom_Domain_Here/Enter_the_Tenant_ID_Here. ZastąpEnter_the_Custom_Domain_Here
ciąg domeną niestandardowego adresu URL iEnter_the_Tenant_ID_Here
identyfikatorem dzierżawy. Jeśli nie masz identyfikatora dzierżawy, dowiedz się, jak odczytywać szczegóły dzierżawy. - Dodaj
knownAuthorities
właściwość o wartości [Enter_the_Custom_Domain_Here].
- Zaktualizuj wartość
Po wprowadzeniu zmian w pliku appsettings.json , jeśli domena niestandardowego adresu URL jest login.contoso.com, a identyfikator dzierżawy to aaaabbbb-0000-cccc-1111-dddd2222eeeee, plik powinien wyglądać podobnie do następującego fragmentu kodu:
{
"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": "*"
}
Dodawanie roli i zakresu aplikacji
Wszystkie interfejsy API muszą opublikować co najmniej jeden zakres, nazywany również uprawnieniem delegowanym, aby aplikacje klienckie pomyślnie uzyskały token dostępu dla użytkownika. Interfejsy API powinny również publikować co najmniej jedną rolę aplikacji dla aplikacji, nazywanych również uprawnieniami aplikacji, aby aplikacje klienckie mogły uzyskać token dostępu jako siebie, czyli wtedy, gdy użytkownik nie loguje się.
Określamy te uprawnienia w pliku appsettings.json . W tym samouczku zarejestrowano cztery uprawnienia. ToDoList.ReadWrite i ToDoList.Read jako uprawnienia delegowane oraz ToDoList.ReadWrite.All i ToDoList.Read.All jako uprawnienia aplikacji.
{
"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": "*"
}
Dodawanie schematu uwierzytelniania
Schemat uwierzytelniania jest nazwany, gdy usługa uwierzytelniania jest skonfigurowana podczas uwierzytelniania. W tym artykule używamy schematu uwierzytelniania elementu nośnego JWT. Dodaj następujący kod w pliku Programs.cs, aby dodać schemat uwierzytelniania.
// 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);
Tworzenie modeli
Utwórz folder o nazwie Models w folderze głównym projektu. Przejdź do folderu i utwórz plik o nazwie ToDo.cs następnie dodaj następujący kod. Ten kod tworzy model o nazwie 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;
}
Dodawanie kontekstu bazy danych
Kontekst bazy danych jest klasą główną, która koordynuje funkcje programu Entity Framework dla modelu danych. Ta klasa jest tworzona przez wyprowadzanie z klasy Microsoft.EntityFrameworkCore.DbContext . W tym samouczku używamy bazy danych w pamięci do celów testowych.
Utwórz folder o nazwie DbContext w folderze głównym projektu.
Przejdź do tego folderu i utwórz plik o nazwie ToDoContext.cs następnie dodaj następującą zawartość do tego pliku:
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; } }
Otwórz plik Program.cs w folderze głównym aplikacji, a następnie dodaj następujący kod w pliku. Ten kod rejestruje podklasę
DbContext
o nazwieToDoContext
jako usługę o określonym zakresie w dostawcy usługi aplikacji ASP.NET Core (znany również jako kontener wstrzykiwania zależności). Kontekst jest skonfigurowany do używania bazy danych w pamięci.// Add the following to your imports using ToDoListAPI.Context; using Microsoft.EntityFrameworkCore; builder.Services.AddDbContext<ToDoContext>(opt => opt.UseInMemoryDatabase("ToDos"));
Dodawanie kontrolerów
W większości przypadków kontroler będzie miał więcej niż jedną akcję. Zazwyczaj akcje Tworzenia, odczytu, aktualizacji i usuwania (CRUD). W tym samouczku utworzymy tylko dwa elementy akcji. Odczytaj cały element akcji i element akcji tworzenia, aby zademonstrować sposób ochrony punktów końcowych.
Przejdź do folderu Controllers w folderze głównym projektu.
Utwórz plik o nazwie ToDoListController.cs wewnątrz tego folderu. Otwórz plik, a następnie dodaj następujący kod płyty kotłowej:
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(){...} }
Dodawanie kodu do kontrolera
W tej sekcji dodajemy kod do utworzonych symboli zastępczych. Tutaj nie skupiamy się na tworzeniu interfejsu API, ale raczej na jego ochronie.
Zaimportuj niezbędne pakiety. Pakiet Microsoft.Identity.Web to otoka biblioteki MSAL, która ułatwia na przykład obsługę logiki uwierzytelniania przez obsługę weryfikacji tokenu. Aby upewnić się, że nasze punkty końcowe wymagają autoryzacji, użyjemy wbudowanego pakietu Microsoft.AspNetCore.Authorization .
Ponieważ przyznaliśmy uprawnienia do wywoływania tego interfejsu API przy użyciu delegowanych uprawnień w imieniu użytkownika lub aplikacji, gdy klient wywołuje się jako sam, a nie w imieniu użytkownika, ważne jest, aby wiedzieć, czy wywołanie jest wykonywane przez aplikację we własnym imieniu. Najprostszym sposobem na to jest ustalenie, czy token dostępu zawiera
idtyp
opcjonalne oświadczenie. Toidtyp
oświadczenie jest najprostszym sposobem dla interfejsu API w celu określenia, czy token jest tokenem aplikacji, czy aplikacją i tokenem użytkownika. Zalecamy włączenie opcjonalnegoidtyp
oświadczenia.idtyp
Jeśli oświadczenie nie jest włączone, możesz użyćroles
oświadczenia iscp
, aby określić, czy token dostępu jest tokenem aplikacji, czy aplikacją i tokenem użytkownika. Token dostępu wystawiony przez Tożsamość zewnętrzna Microsoft Entra ma co najmniej jedno z dwóch oświadczeń. Tokeny dostępu wystawione dla użytkownika mająscp
oświadczenie. Tokeny dostępu wystawione dla aplikacji mająroles
oświadczenie. Tokeny dostępu zawierające oba oświadczenia są wystawiane tylko dla użytkowników, gdziescp
oświadczenie wyznacza delegowane uprawnienia, podczas gdyroles
oświadczenie wyznacza rolę użytkownika. Tokeny dostępu, które nie mają żadnego z nich, nie mają być honorowane.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"); } }
Dodaj funkcję pomocnika, która określa, czy wykonywane żądanie zawiera wystarczające uprawnienia do wykonania zamierzonej akcji. Sprawdź, czy jest to aplikacja wysyłająca żądanie we własnym imieniu, czy też aplikacja wykonuje wywołanie w imieniu użytkownika, który jest właścicielem danego zasobu, sprawdzając identyfikator użytkownika.
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; }
Podłącz definicje uprawnień, aby chronić trasy. Chroń interfejs API, dodając
[Authorize]
atrybut do klasy kontrolera. Dzięki temu akcje kontrolera mogą być wywoływane tylko wtedy, gdy interfejs API jest wywoływany z autoryzowaną tożsamością. Definicje uprawnień określają, jakie rodzaje uprawnień są potrzebne do wykonania tych akcji.[Authorize] [Route("api/[controller]")] [ApiController] public class ToDoListController: ControllerBase{...}
Dodaj uprawnienia do punktu końcowego GET i punktu końcowego POST. W tym celu należy użyć metody RequiredScopeOrAppPermission , która jest częścią przestrzeni nazw Microsoft.Identity.Web.Resource . Następnie przekazujesz zakresy i uprawnienia do tej metody za pomocą atrybutów RequiredScopesConfigurationKey i 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); }
Uruchamianie interfejsu API
Uruchom interfejs API, aby upewnić się, że działa dobrze bez żadnych błędów przy użyciu polecenia dotnet run
. Jeśli zamierzasz używać protokołu HTTPS nawet podczas testowania, musisz ufać usłudze . Certyfikat dewelopera platformy NET.
Pełny przykład tego kodu interfejsu API można znaleźć w pliku przykładów.