ASP.NET Core Identity ile Rol Bazlı Üyelik Sistemi Oluşturmak & Kullanıcıyı Özelleştirmek
Herkese selamlar! Umarım hepiniz iyi ve sağlıklısınızdır. İçinde bulunduğumuz virüs sürecince boş durmamak adına, yeni başlayan arkadaşlar için kritik konuları basit bir şekilde anlattığım makale serime devam etmek istedim.
Kodluyoruz Akademi .NET Core MVC eğitimi içerisinde tamamladığım projede kullandığım Identity kimlik doğrulama yapısını, Identity içerisinde bulunan hazır yapıları isteklerimize göre nasıl değiştirebileceğimizi ve rol bazlı kullanıcı yapısını nasıl oluşturabileceğimizi basitçe anlatmak istiyorum.
ASP.NET Core Identity Nedir?
Microsoft kendi dokümantasyonunda Identity yapısını şöyle tanımlıyor:
- Kullanıcı arayüzü (UI) giriş işlevini destekleyen bir API
- Kullanıcıları, şifreleri, profil verilerini, rolleri, istekleri, tokenları, e-posta onayını ve daha fazlasını yönetir.
Kullanıcılar, Identity içerisinde depolanan oturum açma bilgileriyle bir hesap oluşturabilir veya harici bir oturum açma sağlayıcısı kullanabilirler. Desteklenen harici giriş sağlayacıları arasında Facebook, Google, Microsoft Hesabıve Twitter bulunur ve bu platformlardaki hesaplarınız ile de giriş/kayıt işlemi yapabilirsiniz.
ASP.NET Core Identity ile Kimlik Doğrulamasına Sahip Bir Web Uygulaması Oluşturalım
Visual Studio üzerinde “Create a new project/Yeni proje oluştur” seçeneğini seçelim ve gelen proje türleri arasında “ASP.NET Core Web Application (C#)” türünü seçerek devam edelim.
Gelen ekranda proje adını girdikten ve devam ettikten sonra karşımıza aşağıdaki ekran gelecek. Aşağıdaki ekranda “No Authentication” seçeneği değiştirerek “Individual User Accounts” seçeneğini seçiyoruz ve projemizi oluşturuyoruz.
Bu seçenekle birlikte Identity yapısı ve bir takım ek ayarlar projemize otomatik olarak eklenecektir. Gelin bu bir takım ayarları ve projemize hazır olarak gelen yapıları inceleyelim.
Neler farklı?
Gördüğünüz gibi Identity yapılarının ve sayfalarının tutulacağı Identity alanı Areas klasörü altında oluşturulmuş. Bunun yanında Data içerisinde hazır bir DbContext ve Migration oluşturulduğunu da görüyoruz. Bu context alışa geldiğimiz DbContext sınıfından değil, IdentityDbContext sınıfından türetilmiştir. Bunun sebebi Identity içerisindeki yapıların ve tabloların normal DbContext sınıfından farklı olması ve içerisinde bulunmamasıdır. Migration işlemini veritabanına yansıttığımızda farkı daha iyi anlayacağız.
Aşağıda gördüğünüz gibi appsettings.json içerisinde otomatik olarak bir connection string oluşturulmuş.
Ayrıca aşağıda Startup.cs içerisinde de SqlServer kullanılarak veritabanı ayarları ConfigureServices metodu içerisinde tanımlanmış.
Aşağısındaki AddDefaultIdentity ise Identity ayarlarını servise kaydettiğimiz fonksiyon olarak otomatik olarak oluşturulmuş ve farkettiyseniz IdentityUser adlı bir sınıf kullanılmış bu yapı içerisinde. Bu IdentityUser sınıfı Identity yapısı içerisinde kullanıcımızı temsil eden sınıf olarak kaydedilmiş. İçerisindeki özellikleri birazdan göreceğiz, ileride ise burayı kendi sınıflarımızla güncelleyeceğiz.
Gördüğüniz gibi projenin iskeleti ve temel taşları otomatik olarak oluşturulmuş, yapmamız gereken ilk iş hali hazırda bulunan migration ve ayarları veritabanına yansıtmak.
Bu işlemin ardından migrationlar ve ayarların hepsi veritabanına yansımış olacak, hemen kontrol edelim:
Gördüğünüz gibi veritabanı, hazır ayarlarıyla beraber oluşturuldu. Peki içerisinde neler var?
Evet, Identity yapısının bize sağladığı bütün tablolar bu veritabanı içerisine gelmiş. Bunlar kullanıcıya ait bilgilerin, rollerin ve diğer tüm verilerin saklanması için tasarlanmış tablolar. Bu tablolar içerisinde, başlangıçta bizim için daha önemli olan 3 tablo bulunmaktadır:
- AspNetUsers tablosu kullanıcı bilgilerini tutar.
- AspNetRoles tablosu sistemdeki rolleri tutar.
- AspNetUserRoles tablosu hangi kullanıcıya hangi rolün atandığını tutar.
Projeyi başlatalım ve neye benzediğini görelim:
Login ekranı:
Register ekranı:
Register sayfasından yeni bir kullanıcı oluşturuyorum ve oluşturduğum kullanıcı ile sisteme giriş yapıyorum.
Gördüğünüz gibi sağ üstte giriş yapan kullanıcının bilgisi ve …/Identity/Account/Manage yolunda giriş yapan kullanıcının bilgilerini düzenleyip, yönetebileceğimiz bir Management kısmı mevcut. Buraları ileride düzenleyeceğiz.
Buraya kadar her şey çok güzel, üyelik sistemi bulunan bir uygulama oluşturduk ve bunun için neredeyse hiç bir şey yapmadık. Identity yapısının bize hazır olarak sunduklarının ekmeğini yedik.
Peki ya biz bu hazır yapının projemiz için yeterli olmadığını, kullanıcının farklı özelliklerini de tutabilmemiz gerektiğini ve kullanıcıların rollerinin olması gerektiğini düşüyorsak?
Haklısınız, yukarıda ki register sayfasında görüldüğü gibi kullanıcı sisteme kaydolurken sadece e-posta ve şifre ile kayıt işlemini gerçekleştiriyor. Fakat siz daha büyük ve farklı bir sistem yapmak istiyorsunuz, kullanıcının adı, soyadı, doğum tarihi, telefon numarası, cinsiyeti ve rolü gibi farklı farklı özellikleriyle kaydolmasını istiyorsunuz.
Hazır olarak gelen kullanıcı yapısında sadece soldaki veriler tutuluyor. Yazının en başında bahsettiğim “IdentityUser” sınıfında bulunan özellikler bunlar, rollendirme için ise “IdentityRole” sınıfı kullanılıyor fakat Startup.cs içerisinde bu sınıf default olarak çağırılmamış olarak geliyor yani rol-bazlı bir yapı bulunmamakta.
Haydi gelin biraz kurcalayalım :)
ASP.NET Core Identity Yapısını Özelleştirmek
Öncelikle işe bizi kısıtlayan ve yetersiz bulduğumuz “IdentityUser” sınıfını genişletmekle başlayalım. Bu “IdentityUser” sınıfı içerisindeki özellikleri kullanmak istiyoruz fakat üzerine bir şeyler de eklemek istiyoruz.
Haydi kendi User sınıfımızı oluşturalım o zaman :)
IdentityUser sınıfındaki özellikleri tutmak ve üzerine eklemek istediğimizden yeni user sınıfımızı ondan türeteceğiz. IdentityUser’dan farklı olarak bulundurmak istediğimiz özellikleri yeni kullanıcı sınıfımız olan CustomUser (istediğiniz bir isim olabilir, ApplicationUser, MyUser vb..) sınıfı içerisinde tanımlıyoruz.
Normalde IdentityDbContext default olarak IdentityUser sınıfını kullanıyor fakat biz artık CustomUser’ı kullanacağız:
Artık AspNetUsers tablosu CustomUser sınıfı baz alınarak oluşturulacak/güncellenecek.
Ayrıca Startup.cs içerisinde AddDefaultIdentity içerisinde IdentityUser sınıfını tanımlamıştık, bu kısmı CustomUser olarak değiştiriyoruz ve artık CustomUser sınıfını kullanacağımızı servis konteynerine bildiriyoruz.
Son olarak Shared klasörü altındaki _LoginPartial.cshtml sınıfında IdentityUser sınıfını CustomUser olarak değiştiriyoruz:
Değişikliklerin veritabanına yansıması için;
PM > add-migration MigrationName
PM > update-database
Veritabanının son görüntüsüne bakalım:
En aşağıda gördüğümüz gibi CustomUser sınıfı içerisinde tanımladığımız özellikler veritabanına yansımış. Artık login/register ve kullanıcının bilgilerini çekmek istediğimiz bir senaryoda bu özellikleri de kullanabileceğiz.
Kayıt senaryosunda (Register) bu özelliklerin yanında kullanıcıya rol atamasıda yapmak istiyoruz, bunun için ise yapmamız gereken birkaç ayar var. Haydi onlara göz atalım.
Rol bazlı yetkilendirme (Role-based Authentication)
Identity yapısının kullanıcı için default olarak IdentityUser hazır sınıfını kullandığını öğrendik. Rol için ise IdentityRole sınıfını kullanılıyor, biz bu sınıftan aynı IdentityUser sınıfını özelleştirmek için CustomUser sınıfını yarattığımız gibi yeni bir rol sınıfı yaratabiliriz fakat basit bir rol yapısı için hazır sınıf bizim için gayet yeterli. Eğer custom bir rol sınıfı yazacak olursak, kullanıcı sınıfında yaptığımız gibi IdentityDbContext içerisinde bunu belirtmeliyiz çünkü aksi takdirde default olarak IdentityRole sınıfı kullanılacaktır.
Haydi hazır IdentityRole sınıfı üzerinden gidelim, aktif etmek için servisler içerisindeki Identity servisine bunu belirtmemiz lazım:
İşlem tamam, haydi login-register sayfalarını düzenleyelim.
Identity içerisine bütün sayfalar API üzerinden geldiği için default bir sayfayı Pages içerisinde göremezsiniz. Eğer bir sayfayı düzenlemek isterseniz bu sayfayı eklemek zorundasınız.
Herhangi bir klasöre sağ tıklıyoruz ve Add -> New Scaffolded Item seçeneğini seçiyoruz, gelen ekranda sol menüden Identity kısmını seçip Add/Ekle diyoruz. Karşımıza Identity yapısı içerisindeki tüm sayfalar/yapılar gelecektir:
Burada “Account/Login”, “Account/Register” ve “Account/Manage/Index” sayfalarını veya düzenlemek istediğiniz sayfaları seçtikten sonra contextimizi seçiyoruz ve ekle diyoruz. Artık bu sayfalar görünür ve düzenlenebilir hale gelecektir:
Register Ekranını Düzenleyelim
Normal kayıt ekranında sadece e-posta ve şifre ile kaydoluyorduk, bunu genişletelim. Kayıt ekranında alınacak özellikleri tutan InputModel sınıfı Register.cshtml.cs içerisinde default olarak şöyle görünmektedir:
Bu InputModel içerisine, kullanıcı sınıfına eklediğimiz adı & soyadı, adres, cinsiyet ve doğum tarihi özelliklerini ekleyelim.
Burada gördüğünüz gibi kayıt sırasında kullanıcıdan istediğimiz bilgileri InputModel içerisine yerleştirdik. Fakat fazla olan bir şey var: UserName
UserName özelliği zaten Identity yapısında IdentityUser sınıfı içerisinde bulunan bir özellik fakat birazdan göreceğiniz gibi Email ile aynı değeri alacak şeklinde kullanılıyor. Yani kullanıcının girdiği email, aynı zamanda kullanıcının UserName özelliği oluyor. Biz login ekranında email ile giriş yapmak yerine özel bir kullanıcı adı ile giriş yapmak istediğimiz bir senaryo düşünelim. O yüzden InputModel içerisinde UserName özelliğini de ekledik.
Ayrıca kullanıcıya Öğrenci & Öğretmen olmak üzere iki rolden birini seçtirmek istediğim için Role özelliği ekledim.
Register formu post edildiğinde çalışacak olacak OnPostAsync fonksiyonunu inceleyelim:
Gördüğümüz gibi yeni bir CustomUser oluştururken, kullanıcıya sadece UserName, Email ve Password bilgilerini atıyor. Yukarıda bahsettiğimiz gibi UserName, kullanıcının email adresiyle aynı değeri alıyor. Kullanıcı nesnesinin alacağı değerleri değiştiriyoruz:
Yeni CustomUser nesnesini oluşturduktan sonra _userManager.CreateAsync(user, Input.Password) kodu ile o kullanıcıyı ve şifresiyle sisteme kaydederken, AddToRoleAsync(user, Input.Role) fonksiyonu ile de AspNetUserRoles tablosuna kullanıcıyı seçtiği rolüyle birlikte kaydedeceğiz. Bu sayfa üzerinde kayıt sonrası dönüş sayfası ve bir çok ayarı değiştirebiliriz. Bizim sıradaki işlemimiz ise Register sayfasının html tarafını düzenlemek:
Bu form kayıt sayfasında kullanıcının girdiği değerleri ile yeni bir InputModel oluşturacak ve o değerlerle yeni bir kullanıcı oluşturmuş olacağız. Default olarak AspNetRoles tablosuna Öğrenci (Id:1) ve Öğretmen (Id:2) rollerini ekledim. Kayıt sayfasında da cinsiyet ile birlikte rolünü seçmesi için bir dropdown yarattım. Haydi yeni bir kullanıcı eklemeye çalışalım!
Bilgileri onaylayıp kaydol tuşuna bastıktan sonra kullanıcı artık sisteme kaydolmuş olacak!
Kullanıcımız oluşturulurken rolünü de öğretmen (RoleId=2) olarak kaydettik
Bu kullanıcıyla giriş yapmayı deneyelim fakat öncelikle login sayfasını düzenlememiz lazım. Hazır yapıda Email & Password ikilisiyle giriş yapıyorduk, biz bunu login.cshtml ve login.cshtml.cs dosyalarında Email yazan yerleri UserName olarak değiştiriyoruz.
Email yerine kullanıcı adıyla giriş yapmayı deneyelim:
Artık oluşturduğumuz kullanıcı ile ve kullanıcı adıyla giriş yapabiliyoruz.
CustomUser sınıfı ile eklediğimiz özellikleri ve kullanıcının rolünü test etmek adına bunları sağ üstteki kısımda görüntülemeye çalışalım:
Gördüğünüz gibi artık kullanıcının bütün bilgilerine erişebiliyoruz. Rollere göre istediğiniz sayfayı istediğiniz rol için erişilebilir yapıp veya erişime kapatabilirsiniz. [Authorize(Roles = "Öğretmen")]
Evet biraz uzun ve detaylara girdiğim bir yazı oldu fakat sıfırdan detaylı bir şekilde Kimlik Doğrulamasına sahip ve Rol Bazlı bir üyelik sistemi nasıl oluşturulur, hazır Identity yapısını nasıl istediğimiz şekilde düzenleriz buna değinmiş oldum.
Son
Umarım okuyan herkes için faydalı bir içerik olmuştur. Kodlara erişmek isteyen arkadaşlar için github hesabıma projeyi ekledim, aşağıdaki linkten ulaşabilirler. Kendinize iyi bakın, bir sonraki yazıda görüşmek üzere :)
Projenin github linki: https://github.com/semihelitas/identity-for-beginners