Aracılığıyla paylaş


Öğretici: Dış kiracıda kayıtlı ASP.NET Core web API'lerinin güvenliğini sağlama

Bu öğretici serisi, dış kiracıda kayıtlı bir web API'sinin güvenliğini sağlamayı gösterir. Bu öğreticide, hem temsilci izinleri (kapsamlar) hem de uygulama izinlerini (uygulama rolleri) yayımlayan bir ASP.NET Core web API'sini oluşturacaksınız.

Bu öğreticide;

  • Web API'nizi uygulama kayıt ayrıntılarını kullanacak şekilde yapılandırma
  • 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 ve 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 kolaylaştırır.
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.Identity.Web

Uygulama kaydı ayrıntılarını yapılandırma

uygulama 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 uygulama (istemci) kimliğiniz ile 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 .

Özel URL etki alanı kullanma (İsteğe bağlı)

Kimlik doğrulama URL'sini tam olarak markalandıracak özel bir etki alanı kullanın. Kullanıcı açısından bakıldığında, kullanıcılar ciamlogin.com etki alanı adına yeniden yönlendirilmek yerine kimlik doğrulama işlemi sırasında etki alanınızda kalır.

Özel etki alanı kullanmak için şu adımları izleyin:

  1. Dış kiracınızda özel URL etki alanını etkinleştirmek için Dış kiracılardaki uygulamalar için özel URL etki alanlarını etkinleştirme başlığındaki adımları kullanın.

  2. appsettings.json dosyasını açın:

    1. özelliğinin Instance değerini olarak https://Enter_the_Custom_Domain_Here/Enter_the_Tenant_ID_Heregüncelleştirin. değerini özel URL etki alanınızla ve Enter_the_Tenant_ID_Here kiracı kimliğiniz ile değiştirinEnter_the_Custom_Domain_Here. Kiracı kimliğiniz yoksa kiracı ayrıntılarınızı nasıl okuyacağınızı öğrenin.
    2. [Enter_the_Custom_Domain_Here] değerine sahip özellik ekleyinknownAuthorities.

appsettings.json dosyanızda değişiklik yaptıktan sonra, özel URL etki alanınız login.contoso.com ve kiracı kimliğiniz aaaabbbb-0000-cccc-1111-dddd2222eeeee ise, dosyanız aşağıdaki kod parçacığına benzer görünmelidir:

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

Uygulama rolü ve kapsamı ekleme

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

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

{
  "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 düzeni 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 düzenini kullanacağız. Kimlik doğrulama şeması 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ündeki Program.cs dosyasını açın ve dosyaya aşağıdaki kodu ekleyin. Bu kod, ASP.NET Core uygulama hizmeti sağlayıcısına (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ı korumayı göstermek için tüm eylem öğesini okuyun ve eylem öğesi oluşturun.

  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 plakası 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ı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 çağrılması için kullanıcı adına temsilci izinlerini veya istemcinin kullanıcı adına değil kendi adıyla çağırdığı uygulama izinlerini kullanarak çağrılmasını sağladığımız için, ç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 etkin değilse, erişim belirtecinin idtyp roles 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 bulunur scp . Bir uygulamaya verilen erişim belirteçlerinde talep bulunur roles . Her iki talebi de içeren erişim belirteçleri yalnızca kullanıcılara verilir ve bu belirteçler, talebin temsilci izinleri atadığı scp , talep ise roles kullanıcının rolünü atadığı kullanıcılara verilir. 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ğa sahip olan bir kullanıcı adına arama yapıp yapmadığını denetleyin.

    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. özniteliğini denetleyici sınıfına [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{...}
    

    Tüm GET 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 runçalıştırın. Test sırasında bile HTTPS protokolü 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ım