Udostępnij za pośrednictwem


Samouczek: tworzenie i zabezpieczanie internetowego interfejsu API ASP.NET Core za pomocą platformy tożsamości firmy Microsoft

pl-PL: Dotyczy: Zielony okrąg z białym symbolem zaznaczenia. Najemcy Workforce Zielony okrąg z białym symbolem zaznaczenia. Zewnętrzni najemcy (więcej informacji)

W tej serii samouczków pokazano, jak chronić internetowy interfejs API ASP.NET Core za pomocą platformy tożsamości firmy Microsoft, aby ograniczyć dostęp tylko do autoryzowanych użytkowników i aplikacji klienckich. Kompilowany internetowy interfejs API używa uprawnień delegowanych (zakresów) i uprawnień aplikacji (ról aplikacji).

W tym samouczku nauczysz się następujących rzeczy:

  • Tworzenie internetowego interfejsu API platformy ASP.NET Core
  • Skonfiguruj internetowy interfejs API, aby używać szczegółów rejestracji aplikacji Microsoft Entra
  • Chroń punkty końcowe interfejsu API
  • Uruchom internetowy interfejs API, aby upewnić się, że nasłuchuje żądań HTTP

Warunki wstępne

Tworzenie nowego projektu internetowego interfejsu API platformy ASP.NET Core

Aby utworzyć minimalny projekt internetowego interfejsu API platformy ASP.NET Core, wykonaj następujące kroki:

  1. Otwórz terminal w programie Visual Studio Code lub innym edytorze kodu i przejdź do katalogu, w którym chcesz utworzyć projekt.

  2. Uruchom następujące polecenia w interfejsie wiersza polecenia platformy .NET lub innym narzędziu wiersza polecenia.

    dotnet new web -o TodoListApi
    cd TodoListApi
    
  3. Wybierz pozycję Tak, gdy zostanie wyświetlone okno dialogowe z pytaniem, czy chcesz ufać autorom.

  4. Wybierz pozycję Tak Gdy w oknie dialogowym zostanie wyświetlone pytanie, czy chcesz dodać wymagane zasoby do projektu.

Instalowanie wymaganych pakietów

Aby skompilować, chronić i przetestować internetowy interfejs API platformy ASP.NET Core, należy zainstalować następujące pakiety:

  • Microsoft.EntityFrameworkCore.InMemory— Pakiet, który umożliwia korzystanie z programu Entity Framework Core z bazą danych w pamięci. Jest to przydatne do celów testowych, ale nie jest przeznaczone do użytku produkcyjnego.
  • Microsoft.Identity.Web — zestaw bibliotek ASP.NET Core, które upraszczają dodawanie obsługi uwierzytelniania i autoryzacji do aplikacji internetowych i internetowych interfejsów API integrujących się z platformą tożsamości firmy Microsoft.

Aby zainstalować pakiet, użyj:

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, które zapisałeś po zarejestrowaniu interfejsu API sieci.

{
    "AzureAd": {
        "Instance": "Enter_the_Authority_URL_Here",
        "TenantId": "Enter_the_Tenant_Id_Here",
        "ClientId": "Enter_the_Application_Id_Here"
    },
    "Logging": {...},
  "AllowedHosts": "*"
}

Zastąp następujące elementy zastępcze zgodnie z poniższymi instrukcjami.

  • Zastąp Enter_the_Application_Id_Here identyfikatorem aplikacji (klienta).
  • Zastąp Enter_the_Tenant_Id_Here swoim identyfikatorem katalogu (dzierżawcy).
  • Zastąp Enter_the_Authority_URL_Here adresem URL autorytetu, jak wyjaśniono w następnej sekcji.

Adres URL autoryzacji dla aplikacji

Adres URL urzędu określa katalog, z którego biblioteka Microsoft Authentication Library (MSAL) może żądać tokenów. Budowanie odbywa się inaczej w przypadku zarówno siły roboczej, jak i najemców zewnętrznych, jak pokazano.

//Instance for workforce tenant
Instance: "https://login.microsoftonline.com/"

Użyj niestandardowej domeny adresu URL (opcjonalnie)

Niestandardowe domeny adresów URL nie są obsługiwane w dzierżawach dla zespołów pracowniczych.

Dodawanie uprawnień

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, nazywaną również uprawnieniami aplikacji, aby aplikacje klienckie mogły uzyskać token dostępu w imieniu siebie samych, czyli wtedy, gdy nie logują się użytkownikiem.

Określamy te uprawnienia w pliku appsettings.json. W tym samouczku zarejestrowałeś następujące uprawnienia delegowane i aplikacyjne:

  • Delegowane uprawnienia:ToDoList.Read i ToDoList.ReadWrite.
  • Uprawnienia aplikacji:ToDoList.Read.All i ToDoList.ReadWrite.All.

Gdy użytkownik lub aplikacja kliencka wywołuje internetowy interfejs API, tylko klienci z tymi zakresami lub uprawnieniami uzyskują uprawnienia dostępu do chronionego punktu końcowego.

{
  "AzureAd": {
    "Instance": "Enter_the_Authority_URL_Here",
    "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": "*"
}

Implementowanie uwierzytelniania i autoryzacji w interfejsie API

Aby skonfigurować uwierzytelnianie i autoryzację, otwórz plik program.cs i zastąp jego zawartość następującymi fragmentami kodu:

Dodawanie schematu uwierzytelniania

W tym interfejsie API używamy schematu elementu nośnego JSON Web Token (JWT) jako domyślnego mechanizmu uwierzytelniania. Użyj metody AddAuthentication, aby zarejestrować schemat uwierzytelniania JWT.

// Add required packages to your imports
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

// Add an authentication scheme
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApi(builder.Configuration);

Tworzenie modelu aplikacji

W folderze głównym projektu utwórz folder o nazwie Models. Przejdź do folderu Models i utwórz plik o nazwie ToDo.cs , a następnie dodaj następujący kod.

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;
}

Powyższy kod tworzy model o nazwie ToDo. Ten model reprezentuje dane zarządzane przez aplikację.

Dodawanie kontekstu bazy danych

Następnie zdefiniujemy klasę kontekstu bazy danych, która koordynuje funkcjonalność programu Entity Framework dla modelu danych. Ta klasa dziedziczy z klasy Microsoft.EntityFrameworkCore.DbContext , która zarządza interakcjami między aplikacją a bazą danych. Aby dodać kontekst bazy danych, wykonaj następujące kroki:

  1. Utwórz folder o nazwie DbContext w folderze głównym projektu.

  2. Przejdź do folderu DbContext i utwórz plik o nazwie ToDoContext.cs , a następnie dodaj następujący kod:

    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; }
    }
    
  3. Otwórz plik Program.cs w folderze głównym projektu i zaktualizuj go przy użyciu następującego kodu:

    // Add the following to your imports
    using ToDoListAPI.Context;
    using Microsoft.EntityFrameworkCore;
    
    //Register ToDoContext as a service in the application
    builder.Services.AddDbContext<ToDoContext>(opt =>
        opt.UseInMemoryDatabase("ToDos"));
    

W poprzednim fragmencie kodu rejestrujemy kontekst bazy danych jako usługę o określonym zakresie w dostawcy usługi aplikacji ASP.NET Core (nazywanego również kontenerem wstrzykiwania zależności). Należy również skonfigurować klasę ToDoContext tak, aby korzystała z bazy danych w pamięci dla interfejsu API listy zadań do wykonania.

Konfigurowanie kontrolera

Kontrolery zazwyczaj implementują akcje Tworzenia, odczytu, aktualizacji i usuwania (CRUD) w celu zarządzania zasobami. Ponieważ ten samouczek koncentruje się bardziej na ochronie punktów końcowych interfejsu API, implementujemy tylko dwa elementy akcji w kontrolerze. Akcja Odczytaj wszystkie, aby pobrać wszystkie elementy To-Do i akcję Utwórz, aby dodać nowy element To-Do. Wykonaj następujące kroki, aby dodać kontroler do projektu:

  1. Przejdź do folderu głównego projektu i utwórz folder o nazwie Controllers.

  2. Utwórz plik o nazwie ToDoListController.cs w folderze Controllers i 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 wyjaśniono, jak dodać kod do kontrolera utworzonego w poprzedniej sekcji. Tutaj koncentrujemy się na ochronie interfejsu API, a nie na tworzeniu go.

  1. Zaimportuj niezbędne pakiety: Pakiet Microsoft.Identity.Web jest opakowaniem MSAL.NET, które ułatwia obsługę logiki uwierzytelniania, takiej jak obsługa walidacji tokenu. Aby upewnić się, że nasze punkty końcowe wymagają autoryzacji, użyjemy wbudowanego Microsoft.AspNetCore.Authorization pakietu.

  2. Ponieważ przyznaliśmy uprawnienia do wywoływania tego interfejsu API, używając uprawnień delegowanych w imieniu użytkownika lub uprawnień aplikacji, gdzie klient działa we własnym imieniu, a nie w imieniu użytkownika, ważne jest, aby wiedzieć, czy wywołanie jest wykonywane przez aplikację we własnym imieniu. Najprostszym sposobem jest znalezienie, czy token dostępu zawiera idtyp opcjonalne oświadczenie. To idtyp oświadczenie jest najprostszym sposobem dla interfejsu API, aby określić, czy token jest tokenem aplikacji, czy tokenem aplikacji i użytkownika. Zalecamy włączenie opcjonalnego idtyp atrybutu.

    idtyp Jeśli oświadczenie nie jest włączone, możesz użyć roles i scp oświadczeń, aby określić, czy token dostępu jest tokenem aplikacji, czy tokenem aplikacji + użytkownika. Token dostępu wystawiony przez Microsoft Entra ID ma co najmniej jedno z dwóch roszczeń. Tokeny dostępu wystawione dla użytkownika mają scp atrybut. Tokeny dostępu wydane dla aplikacji zawierają atrybut roles. Tokeny dostępu zawierające oba oświadczenia są wystawiane tylko dla użytkowników, gdzie scp oświadczenie wyznacza delegowane uprawnienia, podczas gdy roles 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");
        }
    }
    
  3. 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;
        }
    
  4. Wprowadź definicje uprawnień, aby chronić ścieżki. 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 punktów końcowych GET i 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);
    }
    

Konfiguracja middleware API do użycia kontrolera

Następnie skonfigurujemy aplikację do rozpoznawania i używania kontrolerów do obsługi żądań HTTP. program.cs Otwórz plik i dodaj następujący kod, aby zarejestrować usługi kontrolera w kontenerze iniekcji zależności.


builder.Services.AddControllers();

var app = builder.Build();
app.MapControllers();

app.Run();

W poprzednim fragmencie AddControllers() kodu metoda przygotowuje aplikację do korzystania z kontrolerów, rejestrując niezbędne usługi, mapując MapControllers() trasy kontrolera na obsługę przychodzących żądań HTTP.

Uruchamianie interfejsu API

Uruchom interfejs API, aby upewnić się, że działa bez żadnych błędów przy użyciu polecenia dotnet run. Jeśli zamierzasz używać protokołu HTTPS nawet podczas testowania, musisz zaufać certyfikatowi programistycznemu .NET.

  1. Uruchom aplikację, wpisując następujące polecenie w terminalu:

    dotnet run
    
  2. Powinien zostać wyświetlony wynik podobny do poniższego w terminalu, co potwierdza, że aplikacja jest uruchomiona na http://localhost:{port}, i nasłuchuje żądań.

    Building...
    info: Microsoft.Hosting.Lifetime[0]
        Now listening on: http://localhost:{port}
    info: Microsoft.Hosting.Lifetime[0]
        Application started. Press Ctrl+C to shut down.
    ...
    

Strona internetowa http://localhost:{host} wyświetla dane wyjściowe podobne do poniższego obrazu. Dzieje się tak, ponieważ interfejs API jest wywoływany bez uwierzytelniania. Aby wykonać autoryzowane wywołanie, zapoznaj się z Następne kroki, aby uzyskać wskazówki dotyczące uzyskiwania dostępu do chronionego sieciowego interfejsu API.

Zrzut ekranu przedstawiający błąd 401 po uruchomieniu strony internetowej.

Pełny przykład tego kodu interfejsu API można znaleźć w pliku przykładów .

Następne kroki