Share via


Zelfstudie: Een ASP.NET Core-web-API bouwen en beveiligen met het Microsoft Identity Platform

Van toepassing op: Groene cirkel met een wit vinkje dat aangeeft dat de volgende inhoud van toepassing is op werknemerstenants. Werknemerstenants Groene cirkel met een wit vinkje dat aangeeft dat de volgende inhoud van toepassing is op externe tenants. Externe tenants (meer informatie)

Deze reeks zelfstudies laat zien hoe u een ASP.NET Core-web-API beveiligt met het Microsoft Identity Platform om de toegang tot alleen geautoriseerde gebruikers en client-apps te beperken. De web-API die u bouwt, maakt gebruik van zowel gedelegeerde machtigingen (bereiken) als toepassingsmachtigingen (app-rollen).

In deze handleiding leert u:

  • Een ASP.NET Core-web-API bouwen
  • De web-API configureren voor het gebruik van de microsoft Entra-app-registratiegegevens
  • Uw web-API-eindpunten beveiligen
  • Voer de web-API uit om ervoor te zorgen dat deze luistert naar HTTP-aanvragen

Voorwaarden

Een nieuw ASP.NET Core-web-API-project maken

Voer de volgende stappen uit om een minimaal ASP.NET Core-web-API-project te maken:

  1. Open uw terminal in Visual Studio Code of een andere code-editor en navigeer naar de map waar u uw project wilt maken.

  2. Voer de volgende opdrachten uit op de .NET CLI of een ander opdrachtregelprogramma.

    dotnet new web -o TodoListApi
    cd TodoListApi
    
  3. Selecteer Ja wanneer u in een dialoogvenster wordt gevraagd of u de auteurs wilt vertrouwen.

  4. Selecteer Ja Wanneer u in een dialoogvenster wordt gevraagd of u de vereiste assets wilt toevoegen aan het project.

Vereiste pakketten installeren

Als u de ASP.NET Core-web-API wilt bouwen, beveiligen en testen, moet u de volgende pakketten installeren:

  • Microsoft.EntityFrameworkCore.InMemory- Een pakket waarmee u de Entity Framework Core kunt gebruiken met een in-memory database. Het is handig voor testdoeleinden, maar is niet ontworpen voor productiegebruik.
  • Microsoft.Identity.Web - een set ASP.NET Core-bibliotheken die het toevoegen van verificatie- en autorisatieondersteuning vereenvoudigen voor web-apps en web-API's die zijn geïntegreerd met het Microsoft Identity Platform.

Als u het pakket wilt installeren, gebruikt u:

dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.Identity.Web

Details van app-registratie configureren

Open het appsettings.json-bestand in de app-map en voeg de app-registratiegegevens toe die u hebt vastgelegd nadat u de web-API hebt geregistreerd.

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

Vervang de volgende tijdelijke aanduidingen zoals weergegeven:

  • Vervang Enter_the_Application_Id_Here door de id van uw toepassing (client).
  • Vervang Enter_the_Tenant_Id_Here door uw directory-ID (tenant-ID).
  • Vervang Enter_the_Authority_URL_Here door de URL van uw instantie, zoals wordt uitgelegd in de volgende sectie.

URL van de autoriteit voor uw app

De instantie-URL specificeert de directory waarvan Microsoft Authentication Library (MSAL) tokens kan aanvragen. U bouwt het anders qua personeelsbestand en externe huurders, zoals weergegeven.

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

Aangepast URL-domein gebruiken (optioneel)

Aangepaste URL-domeinen worden niet ondersteund in werkplekhuren.

Machtigingen toevoegen

Alle API's moeten minimaal één scope, ook wel gedelegeerde machtiging genoemd, publiceren zodat de client-apps met succes een toegangstoken voor een gebruiker kunnen verkrijgen. API's moeten ook minimaal één app-rol, ook wel toepassingsmachtigingen genoemd, publiceren, zodat de client-apps zelf een toegangstoken kunnen verkrijgen, dat wil gezegd wanneer ze zich niet aanmelden bij een gebruiker.

We geven deze machtigingen op in het appsettings.json-bestand. In deze zelfstudie hebt u de volgende gedelegeerde machtigingen en toepassingsmachtigingen geregistreerd:

  • Gedelegeerde machtigingen:ToDoList.Read en ToDoList.ReadWrite.
  • Toepassingsmachtigingen:ToDoList.Read.All en ToDoList.ReadWrite.All.

Wanneer een gebruiker of clienttoepassing de web-API aanroept, worden alleen clients met deze bereiken of machtigingen geautoriseerd voor toegang tot het beveiligde eindpunt.

{
  "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": "*"
}

Verificatie en autorisatie implementeren in de API

Als u verificatie en autorisatie wilt configureren, opent u het program.cs-bestand en vervangt u de inhoud van de volgende codefragmenten:

Een verificatieschema toevoegen

In deze API gebruiken we het Bearer-schema JSON Web Token (JWT) als het standaardverificatiemechanisme. Gebruik de methode AddAuthentication om het JWT bearer-schema te registreren.

// 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);

Het model van uw app maken

Maak in de hoofdmap van het project een map met de naam Modellen. Navigeer naar de map Modellen en maak een bestand met de naam ToDo.cs en voeg vervolgens de volgende code toe.

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

Met de voorgaande code maakt u een model met de naam ToDo. Dit model vertegenwoordigt gegevens die door de app worden beheerd.

Een databasecontext toevoegen

Vervolgens definiëren we een databasecontextklasse, die de Entity Framework-functionaliteit coördineert voor een gegevensmodel. Deze klasse neemt over van de klasse Microsoft.EntityFrameworkCore.DbContext waarmee interacties tussen de toepassing en de database worden beheerd. Voer de volgende stappen uit om de databasecontext toe te voegen:

  1. Maak een map met de naam DbContext in de hoofdmap van uw project.

  2. Navigeer naar de map DbContext en maak een bestand met de naam ToDoContext.cs en voeg de volgende code toe:

    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. Open het Program.cs-bestand in de hoofdmap van uw project en werk het bij met de volgende code:

    // 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"));
    

In het voorgaande codefragment registreren we DB Context als een scoped service in de ASP.NET Core-toepassingsserviceprovider (ook wel bekend als de container voor afhankelijkheidsinjectie). U configureert ook de ToDoContext klasse voor het gebruik van een in-memory database voor de Takenlijst-API.

Een controller instellen

Controllers implementeren doorgaans CRUD-acties (Create, Read, Update en Delete) om resources te beheren. Omdat deze zelfstudie zich meer richt op het beveiligen van de API-eindpunten, implementeren we slechts twee actie-items in de controller. Een 'Alles lezen'-actie om alle To-Do items op te halen en een 'Maken'-actie om een nieuw To-Do item toe te voegen. Volg deze stappen om een controller toe te voegen aan uw project:

  1. Navigeer naar de hoofdmap van uw project en maak een map met de naam Controllers.

  2. Maak een bestand met de naam ToDoListController.cs in de map Controllers en voeg de volgende standaardplaatcode toe:

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(){...}
}

Code toevoegen aan de controller

In deze sectie wordt uitgelegd hoe u code toevoegt aan de controller die in de vorige sectie is geïnstalleerd. De focus hier ligt op het beveiligen van de API, niet op het bouwen ervan.

  1. Importeer de benodigde pakketten: Het Microsoft.Identity.Web pakket is een wrapper rond MSAL.NET waarmee we eenvoudig verificatielogica kunnen afhandelen, zoals het afhandelen van tokenvalidatie. Om ervoor te zorgen dat voor onze eindpunten autorisatie is vereist, gebruiken we het ingebouwde Microsoft.AspNetCore.Authorization pakket.

  2. Omdat we machtigingen hebben verleend voor deze API die moet worden aangeroepen met gedelegeerde machtigingen namens de gebruiker of toepassingsmachtigingen waarbij de client zichzelf aanroept en niet namens de gebruiker, is het belangrijk om te weten of de aanroep door de app zelf wordt gedaan. De eenvoudigste manier om dit te doen, is om te bepalen of het toegangstoken de idtyp optionele claim bevat. Deze idtyp claim is de eenvoudigste manier voor de API om te bepalen of een token een app-token is of een app + gebruikerstoken. We raden aan om de idtyp optionele claim in te schakelen.

    Als de idtyp claim niet is ingeschakeld, kunt u de roles en scp claims gebruiken om te bepalen of het toegangstoken een app-token of een app + gebruikerstoken is. Een toegangstoken dat is uitgegeven door Microsoft Entra ID heeft ten minste één van de twee claims. Toegangstokens die zijn uitgegeven aan een gebruiker, hebben de scp claim. Toegangstokens die zijn uitgegeven aan een toepassing, hebben de roles claim. Toegangstokens die beide claims bevatten, worden alleen verleend aan gebruikers, waarbij de scp claim de gedelegeerde machtigingen aanwijst, terwijl de roles claim de rol van de gebruiker aanwijst. Toegangstokens die geen van beide attributen hebben, moeten niet worden gehonoreerd.

    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. Voeg een helperfunctie toe die bepaalt of de aanvraag die wordt gedaan voldoende machtigingen bevat om de beoogde actie uit te voeren. Controleer of de app de aanvraag zelf indient of dat de app de aanroep doet namens een gebruiker die eigenaar is van de opgegeven resource door de gebruikers-id te valideren.

    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. Sluit uw machtigingsdefinities aan om routes te beveiligen. Beveilig uw API door het [Authorize] kenmerk toe te voegen aan de controllerklasse. Dit zorgt ervoor dat de controlleracties alleen kunnen worden aangeroepen als de API wordt aangeroepen met een geautoriseerde identiteit. De machtigingsdefinities definiëren welke soorten machtigingen nodig zijn om deze acties uit te voeren.

    [Authorize]
    [Route("api/[controller]")]
    [ApiController]
    public class ToDoListController: ControllerBase{...}
    

    Voeg machtigingen toe aan de GET- en POST-eindpunten. Gebruik hiervoor de methode RequiredScopeOrAppPermission die deel uitmaakt van de naamruimte Microsoft.Identity.Web.Resource . Vervolgens geeft u bereiken en machtigingen door aan deze methode via de parameters RequiredScopesConfigurationKey en 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);
    }
    

De API-middleware configureren voor het gebruik van de controller

Vervolgens configureren we de toepassing voor het herkennen en gebruiken van controllers voor het verwerken van HTTP-aanvragen. Open het program.cs bestand en voeg de volgende code toe om de controllerservices te registreren in de container voor afhankelijkheidsinjectie.


builder.Services.AddControllers();

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

app.Run();

In het voorgaande codefragment bereidt de AddControllers() methode de toepassing voor op het gebruik van controllers door de benodigde services te registreren terwijl MapControllers() de controllerroutes worden toegewezen om binnenkomende HTTP-aanvragen te verwerken.

Uw API uitvoeren

Voer uw API uit om ervoor te zorgen dat deze zonder fouten wordt uitgevoerd met behulp van de opdracht dotnet run. Als u van plan bent het HTTPS-protocol zelfs tijdens het testen te gebruiken, moet u het ontwikkelingscertificaat van .NET vertrouwen.

  1. Start de toepassing door het volgende in de terminal te typen:

    dotnet run
    
  2. Er zou een uitvoer moeten worden weergegeven in de terminal die lijkt op het onderstaande, waarmee wordt bevestigd dat de toepassing actief is op http://localhost:{port} en luistert naar aanvragen.

    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.
    ...
    

Op de webpagina http://localhost:{host} wordt een uitvoer weergegeven die vergelijkbaar is met de volgende afbeelding. Dit komt doordat de API zonder verificatie wordt aangeroepen. Als u een geautoriseerde aanroep wilt maken, raadpleegt u Volgende stappen voor hulp bij het openen van een beveiligde web-API.

Schermopname met de fout 401 wanneer de webpagina wordt gestart.

Zie het bestand voorbeeldenvoor een volledig voorbeeld van deze API-code.

Volgende stappen