Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

Asp.NET Core – TypeFilterAttribute’u Extend Ederek Özel Yetkilendirme Kontrolü Sağlama

Merhaba,

Biliyorsunuz ki, Asp.NET Core mimarisiyle yapılan çalışmalarda authentication ve authorization işlemleri için Asp.NET Core ekibi tarafından önerilen yaklaşımlar [Authorize] attribute’una dayalı olan politika(policy) bazlı ya da claim bazlı yaklaşımlardır. Evet, bu yaklaşımlar her ne kadar önerilse de her durum için pek esnetilebilir olmadıkları aşikardır. Keza bu yaklaşımların en net eksikliği, belirli bir eyleme karşın dinamik yetkilendirmenin sağlanabilmesi için ihtiyaç duyulan basitçe çözümü sağlayamamalarıdır. Hele hele bir uygulamadaki yüzlerce aksiyona karşın ayrı izinlerin yönetilmesi gereken durumlarda bu yaklaşımlar her bir aksiyona ciddi kod tekrarları gerektirecektirler. Bu da ilgili yaklaşımların kodsal açıdan gereksiz ek yük sorunu getirdiğini göstermektedir. Asp.NET Core security ekibi, yetkilendirme süreçlerinde her daim hususi çözümlerden kaçınılması gerektiğini tavsiye etse de bazı durumlar için özel yetkilendirme operasyonları kaçınılmaz olarak şarttır diyebiliriz. Bizler bu içeriğimizde, özelleştirilmiş yetkilendirme kontrolü oluşturmaya odaklanacak ve bunun için ideal bir yöntemi ele alıyor olacağız.

Yukarıdaki paragrafın ilk satırlarında referans edilen içeriklere gidip şöyle bi incelemede bulunduğunuzda, esasında yetkilendirmeye dair özel yapılandırmaların nasıl olduğunu ve Asp.NET Core mimarisinin doğasına uygun yöntemlerin tarafımızca zaten incelendiğini görmüş olacaksınız. Bu yöntemlere nazaran bu içeriğimizde daha efektif hareket etmemize imkan sağlayacak bir başka yetkilendirme tekniğini sizlerle inceliyor olacağız.

Teknik olarak, TypeFilterAttribute‘u extend edecek ve bu attribute’un kullanıldığı controller’lardaki request süreçlerinde IAuthorizationFilter‘ı otomatik olarak devreye sokarak özelleştirilmiş bir yetkilendirme mekanizması tasarlayacağız.

Şimdi bunun için mukayese edilmek maksadıyla öncelikle aşağıdaki politika bazlı yetkilendirme ile gerçekleştirilmiş olan çalışmayı ele alalım;

    [Route("api/[controller]")]
    [ApiController]
    [Authorize(Policy = "TimeControl")]
    public class UsersController : ControllerBase
    {
        public IActionResult Get()
        {
            return Ok(true);
        }
    }

Bu çalışma için gerekli olan yapılandırmalara dair tüm detayları makalenin sonunda github adresi verilmiş olan örnek projeden inceleyebilirsiniz. Bizler şimdilik buradaki ‘TimeControl’ isimli politikaya odaklanalım ve işlevsel olarak bulunduğumuz zamanın saniyesi 30 ve üstü ise yetkilendirmenin başarılı kabul edileceğini bilelim.

    public class TimeHandler : AuthorizationHandler<TimeRequirement>
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TimeRequirement requirement)
        {
            if (DateTime.UtcNow.Second >= 30)
                context.Succeed(requirement);
            else
                context.Fail();
            return Task.CompletedTask;
        }
    }

Buradaki işlevi TypeFilterAttribute‘unu extend ederek şöyle de gerçekleştirebiliriz;

    public class TimeControlRequirementAttribute : TypeFilterAttribute
    {
        public TimeControlRequirementAttribute() : base(typeof(TimeControlRequirementFilter))
        {
        }
    }
    public class TimeControlRequirementFilter : IAuthorizationFilter, IAsyncAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationFilterContext context)
        {
            if (DateTime.UtcNow.Second < 30)
                context.Result = new UnauthorizedResult();
        }

        public Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            if (DateTime.UtcNow.Second < 30)
                context.Result = new UnauthorizedResult();
            return Task.CompletedTask;
        }
    }

Ve bu yapıyı aşağıdaki gibi kullanmamız yeterli olacaktır.

    [Route("api/[controller]")]
    [ApiController]
    [TimeControlRequirement]
    public class UsersController : ControllerBase
    {
        public IActionResult Get()
        {
            return Ok(true);
        }
    }

Burada custom olarak oluşturduğumuz attribute’a bir parametre girmek isteyebiliriz. Bunu da senaryomuza uygun olarak saniyenin değerini dinamik bir şekilde verebilmeyi sağlayacak şekilde örneklendirebiliriz. Şöyle ki;

    public class TimeControlRequirementAttribute : TypeFilterAttribute
    {
        public TimeControlRequirementAttribute(int second) : base(typeof(TimeControlRequirementFilter))
        {
            Arguments = new object[] { second };
        }
    }

Yukarıdaki kod bloğuna göz atarsanız eğer attribute’un constructor’ından alınan parametre değerini filter’a gönderebilmek için 5. satırdaki ‘Arguments’ property’sini kullanıyoruz. Bu property içerisine verilen object array’i içerisinde ilgili değeri filter’a gönderiyoruz. Ve filter’da da bu değeri constructor üzerinden yakalayarak aşağıdaki gibi istediğimiz noktalarda kullanıyoruz.

    public class TimeControlRequirementFilter : IAuthorizationFilter, IAsyncAuthorizationFilter
    {
        int second;
        public TimeControlRequirementFilter(int second)
            => this.second = second;

        public void OnAuthorization(AuthorizationFilterContext context)
        {
            if (DateTime.UtcNow.Second < second)
                context.Result = new UnauthorizedResult();
        }

        public Task OnAuthorizationAsync(AuthorizationFilterContext context)
        {
            if (DateTime.UtcNow.Second < second)
                context.Result = new UnauthorizedResult();
            return Task.CompletedTask;
        }
    }
    [Route("api/[controller]")]
    [ApiController]
    [TimeControlRequirement(30)]
    public class UsersController : ControllerBase
    {
        public IActionResult Get()
        {
            return Ok(true);
        }
    }

Evet, böylece Asp.NET Core mimarisinde yetkilendirmeyi özelleştirmenin daha farklı, esnek ve güzel bir yolunu değerlendirmiş ve tecrübe etmiş bulunuyoruz.

Komplikasyonu yüksek olan dinamik yetkilendirme çalışmalarında politika bazlı yetkilendirmenin kısır operasyon alanından ve her bir aksiyona karşın kod tekrarına sebebiyet verebilecek yapılandırma tanımlamalarından kurtulup, bu yöntemin manevratik kabiliyetinden istifade etmeniz, sizleri ihtiyaç olan çözüme daha az maliyetle kavuşturacaktır kanaatindeyim.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Not: Örnek çalışmaya aşağıdaki github adresinden erişebilirsiniz.
https://github.com/gncyyldz/AspNetCore.Custom.Authorization.Control.Approach

Bunlar da hoşunuza gidebilir...

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir