Dela via


Självstudie: Skapa och skydda ett ASP.NET Core-webb-API med Microsofts identitetsplattform

Gäller för: Grön cirkel med en vit bockmarkeringssymbol som anger att följande innehåll gäller för personalklienter.Personalklienter Grön cirkel med en vit bockmarkeringssymbol som anger att följande innehåll gäller för externa klienter. Externa klienter (läs mer)

Den här självstudieserien visar hur du skyddar ett ASP.NET Core-webb-API med Microsofts identitetsplattform för att begränsa åtkomsten till endast auktoriserade användare och klientappar. Webb-API:t som du bygger använder både delegerade behörigheter (omfång) och applikationsroller (programbehörigheter).

I den här handledningen kommer du att:

  • Skapa ett ASP.NET Core-webb-API
  • Konfigurera webb-API:n för att använda sina Microsoft Entra-appregistreringsuppgifter
  • Skydda dina webb-API-slutpunkter
  • Kör webb-API:et för att säkerställa att det lyssnar på HTTP-begäranden

Förutsättningar

Skapa ett nytt ASP.NET Core-webb-API-projekt

Följ dessa steg för att skapa ett minimalt ASP.NET Core-webb-API-projekt:

  1. Öppna terminalen i Visual Studio Code eller någon annan kodredigerare och gå till katalogen där du vill skapa projektet.

  2. Kör följande kommandon på .NET CLI eller något annat kommandoradsverktyg.

    dotnet new web -o TodoListApi
    cd TodoListApi
    
  3. Välj Ja när en dialogruta frågar om du vill lita på författarna.

  4. Välj Ja När en dialogruta frågar om du vill lägga till nödvändiga tillgångar i projektet.

Installera nödvändiga paket

Om du vill skapa, skydda och testa ASP.NET Core-webb-API:et måste du installera följande paket:

  • Microsoft.EntityFrameworkCore.InMemory– Ett paket som gör att du kan använda Entity Framework Core med en minnesintern databas. Den är användbar i testsyfte men är inte avsedd för produktionsanvändning.
  • Microsoft.Identity.Web – en uppsättning ASP.NET Core-bibliotek som förenklar tillägg av stöd för autentisering och auktorisering till webbappar och webb-API:er som integreras med Microsofts identitetsplattform.

Om du vill installera paketet använder du:

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

Konfigurera information om appregistrering

Öppna appsettings.json-filen i din appmapp och lägg till den appregistreringsinformation som du registrerade efter registreringen av webb-API:et.

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

Ersätt följande platshållare enligt följande:

  • Ersätt Enter_the_Application_Id_Here med ditt program-ID (klient-ID).
  • Ersätt Enter_the_Tenant_Id_Here med ditt katalog-ID (Directory ID).
  • Ersätt Enter_the_Authority_URL_Here med din utfärdar-URL, enligt beskrivningen i nästa avsnitt.

Myndighets-URL för din app

Utfärdarens URL anger den katalog som Microsoft Authentication Library (MSAL) kan begära tokens från. Du bygger det annorlunda både när det gäller arbetskraft och externa hyresgäster, som visas:

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

Använda anpassad URL-domän (valfritt)

Anpassade URL-domäner stöds inte i personalklienter.

Lägga till behörigheter

Alla API:n måste publicera minst ett omfång, även kallat delegerad behörighet, för att klientapparna ska kunna få en åtkomsttoken för en användare. API:er bör också publicera minst en applikationsroll, även kallad applikationsbehörigheter, för att klientapparna ska kunna hämta en åtkomsttoken i sitt eget namn, det vill säga när de inte loggar in en användare.

Vi anger dessa behörigheter i filen appsettings.json. I den här självstudien har du registrerat följande delegerade behörigheter och programbehörigheter:

  • Delegerade behörigheter:ToDoList.Read och ToDoList.ReadWrite.
  • Programbehörigheter:ToDoList.Read.All och ToDoList.ReadWrite.All.

När en användare eller ett klientprogram anropar webb-API:et får endast klienter med dessa omfång eller behörigheter behörighet att komma åt den skyddade slutpunkten.

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

Implementera autentisering och auktorisering i API:et

Om du vill konfigurera autentisering och auktorisering öppnar du program.cs-filen och ersätter dess innehåll med följande kodfragment:

Lägga till ett autentiseringsschema

I det här API:et använder vi JSON Web Token (JWT) Bearer-schemat som standardautentiseringsmekanism. Använd metoden AddAuthentication för att registrera JWT-ägarschemat.

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

Skapa appens modell

Skapa en mapp med namnet Modeller i projektets rotmapp. Gå till mappen Modeller och skapa en fil med namnet ToDo.cs och lägg sedan till följande 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;
}

Föregående kod skapar en modell med namnet ToDo. Den här modellen representerar data som appen hanterar.

Lägga till en databaskontext

Därefter definierar vi en databaskontextklass som samordnar Entity Framework-funktionerna för en datamodell. Den här klassen ärver från klassen Microsoft.EntityFrameworkCore.DbContext som hanterar interaktioner mellan programmet och databasen. Följ dessa steg för att lägga till databaskontexten:

  1. Skapa en mapp med namnet DbContext i rotmappen för projektet.

  2. Navigera till mappen DbContext och skapa en fil med namnet ToDoContext.cs och lägg sedan till följande 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. Öppna Program.cs-filen i projektets rotmapp och uppdatera den med följande kod:

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

I föregående kodfragment registrerar vi DB Context som en begränsad tjänst i ASP.NET Core-programtjänstprovidern (även kallad containern för beroendeinmatning). Du konfigurerar också klassen så att den ToDoContext använder en minnesintern databas för ToDo List-API:et.

Ställ in en kontrollenhet

Styrenheter implementerar vanligtvis CRUD-åtgärder (Create, Read, Update och Delete) för att hantera resurser. Eftersom den här handledningen fokuserar mer på att skydda API-slutpunkterna implementerar vi bara två åtgärder i kontrollern. En Läs alla-åtgärd för att hämta alla To-Do objekt och en skapa-åtgärd för att lägga till ett nytt To-Do objekt. Följ de här stegen för att lägga till en kontrollant i projektet:

  1. Gå till rotmappen för projektet och skapa en mapp med namnet Controllers.

  2. Skapa en fil med namnet ToDoListController.cs i mappen Controllers och lägg till följande pannplåtskod:

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

Lägga till kod i kontrollanten

I det här avsnittet förklaras hur du lägger till kod i den kontroller som genererades i föregående avsnitt. Fokus här ligger på att skydda API:et, inte att bygga det.

  1. Importera nödvändiga paket: Paketet Microsoft.Identity.Web är en omslutning runt MSAL.NET som hjälper oss att enkelt hantera autentiseringslogik, till exempel hantering av tokenverifiering. För att säkerställa att våra slutpunkter kräver auktorisering använder vi det inbyggda Microsoft.AspNetCore.Authorization paketet.

  2. Eftersom vi har beviljat behörigheter för att det här API:et ska anropas antingen med delegerade behörigheter för användarens eller programmets behörigheter där klienten anropar som sig själv och inte för användarens räkning, är det viktigt att veta om anropet görs av appen för egen räkning. Det enklaste sättet att göra detta är att ta reda på om åtkomsttoken innehåller det valfria anspråket idtyp . Det här idtyp anspråket är det enklaste sättet för API:et att avgöra om en token är en apptoken eller en app + användartoken. Vi rekommenderar att du aktiverar det idtyp valfria anspråket.

    Om anspråket idtyp inte är aktiverat kan du använda anspråken roles och scp för att avgöra om åtkomsttoken är en apptoken eller en app + användartoken. En åtkomsttoken som utfärdats av Microsoft Entra ID har minst ett av de två anspråken. Åtkomsttoken som utfärdas till en användare har anspråket scp. De åtkomsttoken som utfärdas till ett program har anspråket roles. Åtkomsttoken som innehåller båda anspråken utfärdas endast till användare, där anspråket scp anger de delegerade behörigheterna, medan anspråket roles anger användarens roll. Åtkomsttoken som saknar bådadera ska inte respekteras.

    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. Lägg till en hjälpfunktion som avgör om begäran som görs innehåller tillräckligt med behörigheter för att utföra den avsedda åtgärden. Kontrollera om det är appen som gör begäran för egen räkning eller om appen gör anropet åt en användare som äger den angivna resursen genom att verifiera användar-ID:t.

    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. Anslut dina behörighetsdefinitioner för att skydda vägar. Skydda ditt API genom att lägga till [Authorize] attributet i kontrollantklassen. Detta säkerställer att kontrollantåtgärderna endast kan anropas om API:et anropas med en auktoriserad identitet. Behörighetsdefinitionerna definierar vilka typer av behörigheter som krävs för att utföra dessa åtgärder.

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

    Lägg till behörigheter till GET- och POST-slutpunkterna. Gör detta med metoden RequiredScopeOrAppPermission som ingår i namnområdet Microsoft.Identity.Web.Resource . Sedan skickar du omfång och behörigheter till den här metoden via attributen RequiredScopesConfigurationKey och 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);
    }
    

Konfigurera API-mellanprogrammet så att det använder kontrollanten

Därefter konfigurerar vi programmet för att identifiera och använda kontrollanter för hantering av HTTP-begäranden. program.cs Öppna filen och lägg till följande kod för att registrera kontrollanttjänsterna i containern för beroendeinmatning.


builder.Services.AddControllers();

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

app.Run();

I föregående kodfragment förbereder AddControllers()-metoden programmet för att använda kontrollanter genom att registrera nödvändiga tjänster, medan MapControllers() mappas kontrollantvägarna för att hantera inkommande HTTP-begäranden.

Kör ditt API

Kör api:et för att säkerställa att det körs utan fel med hjälp av kommandot dotnet run. Om du tänker använda HTTPS-protokollet även under testningen måste du lita på .NET:s utvecklingscertifikat.

  1. Starta programmet genom att skriva följande i terminalen:

    dotnet run
    
  2. Utdata som liknar följande ska visas i terminalen, vilket bekräftar att programmet körs på http://localhost:{port} och lyssnar efter begäranden.

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

Webbsidan http://localhost:{host} visar utdata som liknar följande bild. Det beror på att API:et anropas utan autentisering. Om du vill göra ett auktoriserat anrop kan du läsa Nästa steg för vägledning om hur du får åtkomst till ett skyddat webb-API.

Skärmbild som visar 401-felet när webbsidan startas.

Ett fullständigt exempel på den här API-koden finns i -exempelfilen.

Nästa steg