Öğ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
En az bir kapsamı (temsilci izinleri) ve ToDoList.Read gibi bir uygulama rolünü (uygulama izni) kullanıma sunan bir API kaydı. Henüz yapmadıysanız, kayıt adımlarını izleyerek Microsoft Entra yönetim merkezine bir API kaydedin. Aşağıdakilere sahip olduğunuzdan emin olun:
- Web API'sinin uygulama (istemci) kimliği
- Web API'sinin dizin (kiracı) kimliği kaydedildi
- Web API'sinin kaydedildiği dizin (kiracı) alt etki alanı. Örneğin, birincil etki alanınızcontoso.onmicrosoft.com ise, Dizin (kiracı) alt etki alanınız contoso olur.
- Web API'sinin sunduğu temsilci izinleri (kapsamlar) olarak ToDoList.Read ve ToDoList.ReadWrite.
- Web API'sinin kullanıma sunduğu uygulama izinleri (uygulama rolleri) olarak ToDoList.Read.All ve ToDoList.ReadWrite.All.
.NET 7.0 veya üzeri.
Visual Studio Code veya başka bir kod düzenleyicisi.
ASP.NET Core web API'si oluşturma
Terminalinizi açın, ardından projenizin yaşamasını istediğiniz klasöre gidin.
Aşağıdaki komutları çalıştırın:
dotnet new webapi -o ToDoListAPI cd ToDoListAPI
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.Web
Microsoft 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.
Projenin kök klasöründe DbContext adlı bir klasör oluşturun.
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; } }
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
birDbContext
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.
Projenizin kök klasöründeki Denetleyiciler klasörüne gidin.
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.
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.
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. Buidtyp
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ştirmeniziidtyp
öneririz.Talep etkinleştirilmediyse erişim belirtecinin
idtyp
roles
bir uygulama belirteci mi yoksa uygulama + kullanıcı belirteci mi olduğunu belirlemek için vescp
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ırscp
. Bir uygulamaya verilen erişim belirteçlerinin talebi vardırroles
. Her iki talebi içeren erişim belirteçleri yalnızca kullanıcılarascp
verilir ve talep temsilci izinlerini, talep iseroles
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"); } }
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; }
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 run
kullanı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.