Öğretici: Müşteri kiracısında kayıtlı ASP.NET web API'lerinin güvenliğini sağlama

Web API'leri, kullanıcı kimlik doğrulaması ve yetkilendirme gerektiren bilgiler içerebilir. Uygulamalar, korumalı web API'lerini çağırırken yalnızca uygulamanın kendi kimliği olarak hareket ederek, oturum açmış bir kullanıcı adına hareket eden temsilci erişimi veya yalnızca uygulama erişimi kullanabilir.

Bu öğreticide, hem temsilci izinlerini (kapsamlar) hem de uygulama izinlerini (uygulama rolleri) yayımlayan bir web API'sini derleyeceğiz. Oturum açmış bir kullanıcı adına belirteç alan web uygulamaları gibi istemci uygulamaları temsilci izinlerini kullanır. Kendileri için belirteç alan daemon uygulamaları gibi istemci uygulamaları, uygulama izinlerini kullanır.

Bu öğreticide şunların nasıl yapıldığını öğreneceksiniz:

Web API'nizi yapılandırma tp'nizin uygulama kayıt ayrıntılarını kullanma Web API'nizi uygulama kaydında kayıtlı temsilci ve uygulama izinlerini kullanacak şekilde yapılandırma Web API'nizin uç noktalarını koruma

Önkoşullar

ASP.NET Core web API'si oluşturma

  1. Terminalinizi açın, ardından projenizin yaşamasını istediğiniz klasöre gidin.

  2. Aşağıdaki komutları çalıştırın:

    dotnet new webapi -o ToDoListAPI
    cd ToDoListAPI
    
  3. Bir iletişim kutusu projeye gerekli varlıkları eklemek isteyip istemediğinizi sorduğunda Evet'i seçin.

Paketleri yükleme

Aşağıdaki paketleri yükleyin:

  • Microsoft.EntityFrameworkCore.InMemory Entity Framework Core'un bellek içi veritabanıyla kullanılmasını sağlar. Üretim kullanımı için tasarlanmamıştır.
  • Microsoft.Identity.WebMicrosoft kimlik platformu ile tümleştirilen web uygulamalarına ve web API'lerine kimlik doğrulaması ve yetkilendirme desteği eklemeyi basitleştirir.
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.Identity.Web

Uygulama kayıt ayrıntılarını yapılandırma

App klasörünüzde appsettings.json dosyasını açın ve web API'nizi kaydettikten sonra kaydettiğiniz uygulama kayıt ayrıntılarını ekleyin.

{
    "AzureAd": {
        "Instance": "https://Enter_the_Tenant_Subdomain_Here.ciamlogin.com/",
        "TenantId": "Enter_the_Tenant_Id_Here",
        "ClientId": "Enter_the_Application_Id_Here",
    },
    "Logging": {...},
  "AllowedHosts": "*"
}

Aşağıdaki yer tutucuları gösterildiği gibi değiştirin:

  • değerini uygulamanızın (istemci) kimliğiyle değiştirin Enter_the_Application_Id_Here .
  • değerini Dizin (kiracı) kimliğiniz ile değiştirin Enter_the_Tenant_Id_Here .
  • değerini Dizin (kiracı) alt etki alanınızla değiştirin Enter_the_Tenant_Subdomain_Here .

Uygulama rolü ve kapsamı ekleme

İstemci uygulamalarının bir kullanıcı için erişim belirtecini başarıyla alabilmesi için tüm API'lerin en az bir kapsam (temsilci izni olarak da adlandırılır) yayımlaması gerekir. API'ler ayrıca, istemci uygulamalarının bir kullanıcıda oturum açmadıkları durumlarda erişim belirtecini kendileri olarak almaları için uygulamalar için uygulama izni olarak da adlandırılan en az bir uygulama rolü yayımlamalıdır.

Bu izinleri appsettings.json dosyasında belirtiriz. Bu öğreticide dört izin kaydettik. ToDoList.ReadWrite ve ToDoList.Read temsilci izinleri olarak, ToDoList.ReadWrite.All ve ToDoList.Read.All ise uygulama izinleri olarak.

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

Kimlik doğrulama şeması ekleme

Kimlik doğrulama hizmeti kimlik doğrulaması sırasında yapılandırıldığında bir kimlik doğrulama düzeni adlandırılır. Bu makalede JWT taşıyıcı kimlik doğrulama şemasını kullanacağız. Bir kimlik doğrulama düzeni eklemek için Programs.cs dosyasına aşağıdaki kodu ekleyin.

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

Modellerinizi oluşturma

Projenizin kök klasöründe Models adlı bir klasör oluşturun. klasörüne gidin ve ToDo.cs adlı bir dosya oluşturun ve aşağıdaki kodu ekleyin. Bu kod ToDo adlı bir model oluşturur.

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

Veritabanı bağlamı ekleme

Veritabanı bağlamı, bir veri modeli için Entity Framework işlevselliğini koordine eden ana sınıftır. Bu sınıf , Microsoft.EntityFrameworkCore.DbContext sınıfından türetilerek oluşturulur. Bu öğreticide, test amacıyla bellek içi veritabanı kullanacağız.

  1. Projenin kök klasöründe DbContext adlı bir klasör oluşturun.

  2. Bu klasöre gidin ve ToDoContext.cs adlı bir dosya oluşturun ve ardından bu dosyaya aşağıdaki içeriği ekleyin:

    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. Uygulamanızın kök klasöründe Program.cs dosyasını açın, ardından dosyaya aşağıdaki kodu ekleyin. Bu kod, ASP.NET Core uygulama hizmet sağlayıcısında (bağımlılık ekleme kapsayıcısı olarak da bilinir) kapsamlı hizmet olarak adlandırılan ToDoContext bir DbContext alt sınıfı kaydeder. Bağlam, bellek içi veritabanını kullanacak şekilde yapılandırılır.

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

Denetleyici ekleme

Çoğu durumda denetleyicinin birden fazla eylemi olur. Genellikle Oluşturma, Okuma, Güncelleştirme ve Silme (CRUD) eylemleri. Bu öğreticide yalnızca iki eylem öğesi oluşturacağız. Uç noktalarınızın nasıl korunacağını göstermek için tüm eylem öğesini okuma ve eylem öğesi oluşturma.

  1. Projenizin kök klasöründeki Denetleyiciler klasörüne gidin.

  2. Bu klasörün içinde ToDoListController.cs adlı bir dosya oluşturun. Dosyasını açın ve aşağıdaki kazan plaka kodunu ekleyin:

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

Denetleyiciye kod ekleme

Bu bölümde, oluşturduğumuz yer tutuculara kod ekleyeceğiz. Burada odak, API'yi oluşturmak değil, korumaktır.

  1. Gerekli paketleri içeri aktarın. Microsoft.Identity.Web paketi, kimlik doğrulama mantığını, örneğin belirteç doğrulamasını işleyerek kolayca işlememize yardımcı olan bir MSAL sarmalayıcısıdır. Uç noktalarımızın yetkilendirme gerektirdiğinden emin olmak için yerleşik Microsoft.AspNetCore.Authorization paketini kullanırız.

  2. Bu API'nin kullanıcı adına temsilci izinleri kullanılarak çağrılması için izinler verildiğinden veya istemcinin kendi adına çağırdığı uygulama izinleri kullanıldığından, çağrının uygulama tarafından kendi adına yapılıp yapılmadığını bilmek önemlidir. Bunu yapmanın en kolay yolu, erişim belirtecinin isteğe bağlı talebi içerip içermediğini idtyp bulma talepleridir. Bu idtyp talep, API'nin belirtecin uygulama belirteci mi yoksa uygulama + kullanıcı belirteci mi olduğunu belirlemesinin en kolay yoludur. İsteğe bağlı talebi etkinleştirmenizi idtyp öneririz.

    Talep etkinleştirilmediyse erişim belirtecinin idtyproles bir uygulama belirteci mi yoksa uygulama + kullanıcı belirteci mi olduğunu belirlemek için ve scp taleplerini kullanabilirsiniz. Microsoft Entra Dış Kimlik tarafından verilen erişim belirtecinin iki talep arasında en az biri vardır. Kullanıcıya verilen erişim belirteçlerinde talep vardır scp . Bir uygulamaya verilen erişim belirteçlerinin talebi vardır roles . Her iki talebi içeren erişim belirteçleri yalnızca kullanıcılara scp verilir ve talep temsilci izinlerini, talep ise roles kullanıcının rolünü belirleyebilir. Hiçbiri olmayan erişim belirteçleri kabul edilmez.

    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. Yapılan isteğin istenen eylemi gerçekleştirmek için yeterli izinler içerip içermediğini belirleyen bir yardımcı işlev ekleyin. İsteği kendi adına yapan uygulamanın mı yoksa kullanıcının kimliğini doğrulayarak verilen kaynağın sahibi olan bir kullanıcı adına arama yapıp yapmadığından emin olun.

    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. Yolları korumak için izin tanımlarınızı takın. Denetleyici sınıfına özniteliğini [Authorize] ekleyerek API'nizi koruyun. Bu, denetleyici eylemlerinin yalnızca API yetkili bir kimlikle çağrıldığında çağrılabilmesini sağlar. İzin tanımları, bu eylemleri gerçekleştirmek için gereken izin türlerini tanımlar.

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

    Get all uç noktasına ve POST uç noktasına izinler ekleyin. Bunu, Microsoft.Identity.Web.Resource ad alanının parçası olan RequiredScopeOrAppPermission yöntemini kullanarak yapın. Ardından RequiredScopesConfigurationKey ve RequiredAppPermissionsConfigurationKey öznitelikleri aracılığıyla bu yönteme kapsamları ve izinleri geçirirsiniz.

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

API'nizi çalıştırma

komutunu kullanarak api'nizin sorunsuz çalıştığından emin olmak için komutunu dotnet runkullanın. Test sırasında bile https protokollerini kullanmayı planlıyorsanız, öğesine güvenmeniz gerekir. NET'in geliştirme sertifikası.

Bu API kodunun tam örneği için örnek dosyasına bakın.

Sonraki adımlar