Sdílet prostřednictvím


Kurz: Zabezpečení webového rozhraní API ASP.NET Core zaregistrovaného v externím tenantovi

Tato série kurzů ukazuje, jak zabezpečit zaregistrované webové rozhraní API v externím tenantovi. V tomto kurzu vytvoříte webové rozhraní API ASP.NET Core, které publikuje delegovaná oprávnění (obory) i oprávnění aplikace (role aplikací).

V tomto kurzu;

  • Konfigurace webového rozhraní API pro použití podrobností o registraci aplikace
  • Nakonfigurujte webové rozhraní API tak, aby používalo delegovaná oprávnění a oprávnění aplikací zaregistrovaná v registraci aplikace.
  • Ochrana koncových bodů webového rozhraní API

Požadavky

Vytvoření webového rozhraní API ASP.NET Core

  1. Otevřete terminál a přejděte do složky, ve které má projekt žít.

  2. Spusťte následující příkazy:

    dotnet new webapi -o ToDoListAPI
    cd ToDoListAPI
    
  3. Když se zobrazí dialogové okno s dotazem, jestli chcete do projektu přidat požadované prostředky, vyberte Ano.

Instalace balíčků

Nainstalujte následující balíčky:

  • Microsoft.EntityFrameworkCore.InMemory umožňuje použití Entity Framework Core s databází v paměti. Není určen pro použití v produkčním prostředí.
  • Microsoft.Identity.Web zjednodušuje přidávání podpory ověřování a autorizace do webových aplikací a webových rozhraní API integrujících se službou Microsoft Identity Platform.
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.Identity.Web

Konfigurace podrobností registrace aplikace

Otevřete soubor appsettings.json ve složce aplikace a přidejte podrobnosti o registraci aplikace, které jste si poznamenali po registraci webového rozhraní 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": "*"
}

Nahraďte následující zástupné symboly, jak je znázorněno:

  • Nahraďte Enter_the_Application_Id_Here ID vaší aplikace (klienta).
  • Nahraďte Enter_the_Tenant_Id_Here ID vašeho adresáře (tenanta).
  • Nahraďte Enter_the_Tenant_Subdomain_Here subdoménou adresáře (tenanta).

Použití vlastní domény URL (volitelné)

Pomocí vlastní domény plně označte adresu URL ověřování. Z pohledu uživatele zůstanou uživatelé ve vaší doméně během procesu ověřování místo přesměrování na ciamlogin.com název domény.

Pokud chcete použít vlastní doménu, postupujte takto:

  1. Pomocí kroků v části Povolit vlastní domény URL pro aplikace v externích tenantech povolte pro externího tenanta vlastní doménu URL.

  2. Otevřete soubor appsettings.json :

    1. Aktualizujte hodnotu Instance vlastnosti na https://Enter_the_Custom_Domain_Here/Enter_the_Tenant_ID_Herehodnotu . Nahraďte Enter_the_Custom_Domain_Here vlastní doménou URL a Enter_the_Tenant_ID_Here ID tenanta. Pokud nemáte ID tenanta, přečtěte si, jak si přečíst podrobnosti o tenantovi.
    2. Přidejte knownAuthorities vlastnost s hodnotou [Enter_the_Custom_Domain_Here].

Po provedení změn souboru appsettings.json , pokud je vaše vlastní doména URL login.contoso.com a ID vašeho tenanta je aaaabbbb-0000-cccc-1111-dddd2222eeeee, měl by váš soubor vypadat podobně jako následující fragment kódu:

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

Přidání role a oboru aplikace

Všechna rozhraní API musí publikovat minimálně jeden obor, označovaný také jako delegovaná oprávnění, aby klientské aplikace získaly přístupový token pro uživatele. Rozhraní API by také měla publikovat minimálně jednu roli aplikace pro aplikace, označovanou také jako oprávnění aplikace, aby klientské aplikace získaly přístupový token jako samy o sobě, tj. když se nepřihlašují uživateli.

Tato oprávnění zadáme do souboru appsettings.json . V tomto kurzu jsme zaregistrovali čtyři oprávnění. ToDoList.ReadWrite a ToDoList.Read jako delegovaná oprávnění a ToDoList.ReadWrite.All a ToDoList.Read.All jako oprávnění aplikace.

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

Přidání schématu ověřování

Schéma ověřování je pojmenováno při konfiguraci ověřovací služby během ověřování. V tomto článku používáme schéma ověřování nosné ověřování JWT. Do souboru Programs.cs přidejte následující kód, který přidá schéma ověřování.

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

Vytvoření modelů

Vytvořte složku s názvem Models v kořenové složce projektu. Přejděte do složky a vytvořte soubor s názvem ToDo.cs přidejte následující kód. Tento kód vytvoří model s názvem 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;
}

Přidání kontextu databáze

Kontext databáze je hlavní třída, která koordinuje funkce Entity Framework pro datový model. Tato třída je vytvořena odvozením z Microsoft.EntityFrameworkCore.DbContext třídy. V tomto kurzu používáme k testování databázi v paměti.

  1. Vytvořte složku s názvem DbContext v kořenové složce projektu.

  2. Přejděte do této složky a vytvořte soubor s názvem ToDoContext.cs do tohoto souboru přidejte následující obsah:

    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. Otevřete soubor Program.cs v kořenové složce aplikace a do souboru přidejte následující kód. Tento kód zaregistruje podtřídu DbContext volanou ToDoContext jako službu s vymezeným oborem ve zprostředkovateli aplikační služby ASP.NET Core (označuje se také jako kontejner injektáže závislostí). Kontext je nakonfigurovaný tak, aby používal databázi v paměti.

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

Přidání kontrolerů

Ve většině případů by kontroler měl více než jednu akci. Obvykle se jedná o akce vytvoření, čtení, aktualizace a odstranění (CRUD). V tomto kurzu vytvoříme pouze dvě položky akce. Přečtěte si všechny položky akce a položku vytvoření akce, abyste si ukázali, jak chránit koncové body.

  1. Přejděte do složky Kontrolery v kořenové složce projektu.

  2. Vytvořte soubor s názvem ToDoListController.cs uvnitř této složky. Otevřete soubor a pak přidejte následující kód kotlového štítku:

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

Přidání kódu do kontroleru

V této části přidáme kód do zástupných symbolů, které jsme vytvořili. Tady je fokus na vytváření rozhraní API, ale spíše na jeho ochranu.

  1. Importujte potřebné balíčky. Balíček Microsoft.Identity.Web je obálka MSAL, která nám pomáhá snadno zpracovat logiku ověřování, například zpracováním ověřování tokenů. Abychom zajistili, že naše koncové body vyžadují autorizaci, použijeme balíček Inbuilt Microsoft.AspNetCore.Authorization .

  2. Vzhledem k tomu, že jsme udělili oprávnění k volání tohoto rozhraní API buď pomocí delegovaných oprávnění jménem uživatele nebo aplikace, kde klient volá jako sám sebe, a ne jménem uživatele, je důležité vědět, jestli volání provádí aplikace vlastním jménem. Nejjednodušší způsob, jak to udělat, je deklarace identity, abyste zjistili, jestli přístupový token obsahuje volitelnou idtyp deklaraci identity. Tato idtyp deklarace identity je nejjednodušší způsob, jak rozhraní API určit, jestli je tokenem aplikace nebo tokenem aplikace + token uživatele. Doporučujeme povolit volitelnou idtyp deklaraci identity.

    idtyp Pokud deklarace identity není povolená, můžete pomocí roles scp deklarací identity určit, jestli se jedná o přístupový token aplikace nebo token aplikace a uživatele. Přístupový token vydaný Microsoft Entra Externí ID má alespoň jednu ze dvou deklarací identity. Přístupové tokeny vydané uživateli mají scp deklaraci identity. Přístupové tokeny vydané pro aplikaci mají roles deklaraci identity. Přístupové tokeny, které obsahují obě deklarace identity, se vydávají jenom uživatelům, kde scp deklarace identity určuje delegovaná oprávnění, zatímco roles deklarace identity určuje roli uživatele. Přístupové tokeny, které nemají být dodrženy ani jeden.

    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. Přidejte pomocnou funkci, která určuje, jestli provedený požadavek obsahuje dostatečná oprávnění k provedení zamýšlené akce. Zkontrolujte, jestli aplikace provádí žádost vlastním jménem nebo jestli aplikace volá jménem uživatele, který daný prostředek vlastní, tím, že ověří ID uživatele.

    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. Připojte definice oprávnění k ochraně tras. Chraňte své rozhraní API přidáním [Authorize] atributu do třídy kontroleru. Tím se zajistí, že akce kontroleru lze volat pouze v případě, že se rozhraní API volá s autorizovanou identitou. Definice oprávnění definují, jaké druhy oprávnění jsou potřeba k provedení těchto akcí.

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

    Přidejte oprávnění ke všem koncovým bodům GET a koncovému bodu POST. Použijte metodu RequiredScopeOrAppPermission , která je součástí oboru názvů Microsoft.Identity.Web.Resource . Pak předáte obory a oprávnění této metodě prostřednictvím RequiredScopesConfigurationKey a RequiredAppPermissionsConfigurationKey atributy.

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

Spuštění rozhraní API

Spuštěním rozhraní API ověřte, že je správně spuštěný bez chyb pomocí příkazu dotnet run. Pokud máte v úmyslu používat protokol HTTPS i během testování, musíte důvěřovat . Vývojový certifikát net

Úplný příklad tohoto kódu rozhraní API najdete v ukázkovém souboru.

Další krok