Bagikan melalui


Tutorial: Membangun dan mengamankan API web ASP.NET Core dengan platform identitas Microsoft

Berlaku untuk: Lingkaran hijau dengan simbol tanda centang putih. Penyewa tenaga kerja Lingkaran hijau dengan simbol tanda centang putih. Penyewa eksternal (pelajari lebih lanjut)

Seri tutorial ini menunjukkan cara melindungi API web ASP.NET Core dengan platform identitas Microsoft untuk membatasi aksesnya hanya ke pengguna dan aplikasi klien yang berwenang. API web yang Anda buat menggunakan izin yang didelegasikan (cakupan) dan izin aplikasi (peran aplikasi).

Di tutorial ini, Anda akan:

  • Membangun API web ASP.NET Core
  • Mengonfigurasi API web untuk menggunakan detail pendaftaran aplikasi Microsoft Entra
  • Melindungi titik akhir API web Anda
  • Jalankan API web untuk memastikan api mendengarkan permintaan HTTP

Prasyarat

Membuat proyek API web ASP.NET Core baru

Untuk membuat proyek API web ASP.NET Core minimal, ikuti langkah-langkah berikut:

  1. Buka terminal Anda di Visual Studio Code atau editor kode lainnya dan navigasikan ke direktori tempat Anda ingin membuat proyek.

  2. Jalankan perintah berikut pada .NET CLI atau alat baris perintah lainnya.

    dotnet new web -o TodoListApi
    cd TodoListApi
    
  3. Pilih Ya saat kotak dialog menanyakan apakah Anda ingin mempercayai penulis.

  4. Pilih Ya Saat kotak dialog menanyakan apakah Anda ingin menambahkan aset yang diperlukan ke proyek.

Menginstal paket yang diperlukan

Untuk membangun, melindungi, dan menguji API web ASP.NET Core, Anda perlu menginstal paket berikut:

  • Microsoft.EntityFrameworkCore.InMemory- Paket yang memungkinkan Anda menggunakan Entity Framework Core dengan database dalam memori. Ini berguna untuk tujuan pengujian tetapi tidak dirancang untuk penggunaan produksi.
  • Microsoft.Identity.Web - sekumpulan pustaka ASP.NET Core yang menyederhanakan penambahan dukungan autentikasi dan otorisasi ke aplikasi web dan API web yang terintegrasi dengan platform identitas Microsoft.

Untuk menginstal paket, gunakan:

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

Mengonfigurasi detail pendaftaran aplikasi

Buka file appsettings.json di folder aplikasi Anda dan tambahkan detail pendaftaran aplikasi yang Anda rekam setelah mendaftarkan API web.

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

Ganti tempat penampung berikut seperti yang diperlihatkan:

  • Ganti Enter_the_Application_Id_Here dengan ID aplikasi (klien) Anda.
  • Ganti Enter_the_Tenant_Id_Here dengan ID Direktori (penyewa) Anda.
  • Ganti Enter_the_Authority_URL_Here dengan URL Otoritas Anda, seperti yang dijelaskan di bagian berikutnya.

URL Otoritas untuk aplikasi Anda

URL otoritas menentukan direktori tempat Microsoft Authentication Library (MSAL) dapat meminta token. Anda membuatnya secara berbeda di penyewa tenaga kerja dan eksternal, seperti yang ditunjukkan:

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

Menggunakan domain URL kustom (Opsional)

Domain URL kustom tidak didukung di penyewa tenaga kerja.

Menambahkan izin

Semua API harus menerbitkan minimal satu cakupan, juga disebut izin yang didelegasikan, agar aplikasi klien mendapatkan token akses bagi pengguna dengan sukses. APIs juga harus menerbitkan minimal satu peran aplikasi, yang juga disebut izin aplikasi, agar aplikasi klien dapat memperoleh token akses atas nama mereka sendiri, yaitu, ketika mereka tidak masuk sebagai pengguna.

Kami menentukan izin ini dalam file appsettings.json. Dalam tutorial ini, Anda mendaftarkan izin yang didelegasikan dan aplikasi berikut:

  • Izin yang didelegasikan:ToDoList.Read dan ToDoList.ReadWrite.
  • Izin aplikasi:ToDoList.Read.All dan ToDoList.ReadWrite.All.

Saat aplikasi pengguna atau klien memanggil API web, hanya klien dengan cakupan atau izin ini yang diizinkan untuk mengakses titik akhir yang dilindungi.

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

Menerapkan autentikasi dan otorisasi di API

Untuk mengonfigurasi autentikasi dan otorisasi, buka file program.cs dan ganti isinya cuplikan kode berikut:

Menambahkan skema autentikasi

Dalam API ini, kami menggunakan skema JSON Web Token (JWT) Bearer sebagai mekanisme autentikasi default. Gunakan metode AddAuthentication untuk mendaftarkan skema pembawa 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);

Membuat model aplikasi Anda

Di folder akar proyek, buat folder bernama Model. Navigasikan ke folder Model dan buat file bernama ToDo.cs lalu tambahkan kode berikut.

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

Kode sebelumnya membuat model yang disebut ToDo. Model ini mewakili data yang dikelola aplikasi.

Tambahkan konteks database

Selanjutnya, kami menentukan kelas konteks database, yang mengoordinasikan fungsionalitas Kerangka Kerja Entitas untuk model data. Kelas ini mewarisi dari kelas Microsoft.EntityFrameworkCore.DbContext yang mengelola interaksi antara aplikasi dan database. Untuk menambahkan konteks database, ikuti langkah-langkah berikut:

  1. Buat folder bernama DbContext di folder akar proyek Anda.

  2. Navigasikan ke folder DbContext dan buat file bernama ToDoContext.cs lalu tambahkan kode berikut:

    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. Buka file Program.cs di folder akar proyek Anda dan perbarui dengan kode berikut:

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

Dalam cuplikan kode sebelumnya, kami mendaftarkan Konteks DB sebagai layanan terlingkup di penyedia layanan aplikasi ASP.NET Core (juga dikenal sebagai, kontainer injeksi dependensi). Anda juga mengonfigurasi ToDoContext kelas untuk menggunakan database dalam memori untuk TODo List API.

Menyiapkan pengontrol

Pengontrol biasanya menerapkan tindakan Buat, Baca, Perbarui, dan Hapus (CRUD) untuk mengelola sumber daya. Karena tutorial ini lebih berfokus pada melindungi titik akhir API, kami hanya menerapkan dua item tindakan di pengontrol. Tindakan Baca semua untuk mengambil semua item To-Do dan tindakan Buat untuk menambahkan item To-Do baru. Ikuti langkah-langkah ini untuk menambahkan pengontrol ke proyek Anda:

  1. Navigasi ke folder akar proyek Anda dan buat folder bernama Pengontrol.

  2. Buat file bernama ToDoListController.cs di dalam folder Pengontrol dan tambahkan kode pelat boiler berikut:

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

Menambahkan kode ke pengontrol

Bagian ini menjelaskan cara menambahkan kode ke pengontrol yang telah disusun di bagian sebelumnya. Fokus di sini adalah melindungi API, bukan membangunnya.

  1. Impor paket yang diperlukan: Paket Microsoft.Identity.Web ini adalah pembungkus di sekitar MSAL.NET yang membantu kami dengan mudah menangani logika autentikasi seperti menangani validasi token. Untuk memastikan bahwa titik akhir kami memerlukan otorisasi, kami menggunakan paket bawaan Microsoft.AspNetCore.Authorization .

  2. Karena kami memberikan izin agar API ini dipanggil baik menggunakan izin yang didelegasikan atas nama pengguna atau izin aplikasi di mana klien memanggil sebagai dirinya sendiri dan bukan atas nama pengguna, penting untuk mengetahui apakah panggilan dilakukan oleh aplikasi atas nama sendiri. Cara term mudah untuk melakukan ini adalah dengan menemukan apakah token akses berisi idtyp klaim opsional. Klaim ini idtyp adalah cara termudah bagi API untuk menentukan apakah token adalah token aplikasi atau token aplikasi + pengguna. Sebaiknya aktifkan idtyp klaim opsional.

    Jika klaim idtyp tidak diaktifkan, Anda dapat menggunakan klaim roles dan scp untuk menentukan apakah token akses adalah token aplikasi atau token aplikasi dan pengguna. Token akses yang dikeluarkan oleh ID Microsoft Entra memiliki setidaknya salah satu dari dua klaim. Token akses yang dikeluarkan untuk pengguna memiliki scp klaim. Token akses yang dikeluarkan ke aplikasi memiliki roles klaim. Token akses yang berisi kedua klaim hanya dikeluarkan untuk pengguna, di mana scp klaim menunjuk izin yang didelegasikan, sementara roles klaim menunjuk peran pengguna. Token akses yang tidak memenuhi kedua syarat tidak boleh dihormati.

    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. Tambahkan fungsi pembantu yang menentukan apakah permintaan yang dibuat berisi izin yang cukup untuk melakukan tindakan yang dimaksudkan. Periksa apakah aplikasi yang membuat permintaan atas nama sendiri atau apakah aplikasi melakukan panggilan atas nama pengguna yang memiliki sumber daya yang diberikan dengan memvalidasi ID pengguna.

    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. Colokkan definisi izin Anda untuk melindungi rute. Lindungi API Anda dengan menambahkan [Authorize] atribut ke kelas pengontrol. Ini memastikan tindakan pengontrol hanya dapat dipanggil jika API dipanggil dengan identitas yang diotorisasi. Definisi izin menentukan jenis izin apa yang diperlukan untuk melakukan tindakan ini.

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

    Tambahkan izin ke titik akhir GET dan POST. Lakukan ini menggunakan metode RequiredScopeOrAppPermission yang merupakan bagian dari namespace Microsoft.Identity.Web.Resource . Anda kemudian meneruskan cakupan dan izin ke metode ini melalui atribut RequiredScopesConfigurationKey dan 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);
    }
    

Mengonfigurasi middleware API untuk menggunakan pengontrol

Selanjutnya, kami mengonfigurasi aplikasi untuk mengenali dan menggunakan pengontrol untuk menangani permintaan HTTP. program.cs Buka file dan tambahkan kode berikut untuk mendaftarkan layanan pengontrol dalam kontainer injeksi dependensi.


builder.Services.AddControllers();

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

app.Run();

Dalam cuplikan kode sebelumnya, AddControllers() metode ini menyiapkan aplikasi untuk menggunakan pengontrol dengan mendaftarkan layanan yang diperlukan sambil MapControllers() memetakan rute pengontrol untuk menangani permintaan HTTP masuk.

Jalankan API Anda

Jalankan API Anda untuk memastikan bahwa API berjalan tanpa kesalahan menggunakan perintah dotnet run. Jika Anda ingin menggunakan protokol HTTPS bahkan selama pengujian, Anda perlu mempercayai sertifikat pengembangan .NET .

  1. Mulai aplikasi dengan mengetik yang berikut ini di terminal:

    dotnet run
    
  2. Output yang mirip dengan berikut ini harus ditampilkan di terminal, yang mengonfirmasi bahwa aplikasi sedang dijalankan di http://localhost:{port} dan mendengarkan permintaan.

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

Halaman web http://localhost:{host} menampilkan output yang mirip dengan gambar berikut. Ini karena API dipanggil tanpa autentikasi. Untuk melakukan panggilan resmi, lihat Langkah berikutnya untuk panduan tentang cara mengakses API web yang dilindungi.

Cuplikan layar yang memperlihatkan kesalahan 401 saat halaman web diluncurkan.

Untuk contoh lengkap kode API ini, lihat file sampel .

Langkah berikutnya