Yine çok hızlı bir yazı ile karşınızdayım. 🙂 Çok derinlere girmeden daha çok kod üzerinden örnekler ve bazı anahtar kelimeler ile JSON Web Token(JWT) konseptini ASP.NET Core projelerinde nasıl uygularız bundan bahsetmeye çalışacağım.
Açıkcası JWT oldukça derin bir konu. Şeması olsun, şeması ile ilişkili özellikler olsun bunların ayrıntılarına çok girmeyeceğim. Çok derinlere girmeden, JWT nedir bununla başlayalım. Client-Server iletişim mimarisinde, sunucu tarafından kimlik doğrulama, yetki tanımlama/doğrulama ve bilgi akışı güvenliği sağlamak için oluşturulmuş bir işaretleme standartı diye basitçe açıklayabilirim. İşaretleme diyerek, sunucu tarafından oluşturulan ve iletilen bir “token” ile işaretlenmiş client‘lar, bu işaret ile kendilerini sunucuya tanıtarak ve doğrulayarak, belli metotları çalıştırabiliyorlar demek istedim. Açıkcası bu şekilde bir ifade ile daha net anlaşılabileceğini düşünüyorum.
HTTP’nin “Stateless” bir protokol olması, “Session” yönetiminin transfer protokollerinde sağlıklı olmaması, dağıtık mimarilerde “Session” ve “State” yönetimin zor olması JWT’nin önünü açan bir durum. Bu bahsetmiş olduğum konular ile ilgili sorunlar ya da ihtiyaçlar ortaya çıkıyorsa JWT’ye göz atmanızda fayda var.
Neyse çok konuştuk… Daha çok kod üzerinden gidecektik, hani nerde? Öncelikle burada bahsedeceğim tüm kodlar, projeler GitHub profilimde mevcut. Direkt oradan da devam edebilirsiniz.
Şimdi gelelim senaryomuza… Bir tane WebAPI uygulamamız olsun. Bu uygulama üzerinden kullanıcı kimliği doğrulaması yapıp, web api uygulamasındaki başka kaynakları çalıştırabilelim. Eğer kimlik doğrulaması yapamıyorsak tabi ki çalıştıramayalım. Bu Web API uygulamasına kimlik doğrulaması için istersek mobilden, istersek de başka bir Web uygulamasından bağlanabileceğimizi de düşünün.
ASP.NET Core ile bu bahsetmiş olduğum senaryoyu nasıl yapacağız buna bakalım bizde. Açıkcası ASP.NET Core’da, kendi içerisindeki API’lar ve middleware yaklaşımı sayesinde JWT oluşturmasını ve doğrulamasını yapmak oldukça kolay.
Bunun için “JSON Web Token” yaratmak ve doğrulamak istediğimiz ASP.NET projemizde ConfigureServices() metoduna öncelikle JWT yapısını kullanacağımızı belirtiyoruz.
Burada önemli olan yerlere kısaca bakalım. AddAuthentication() içinde şema formatının nasıl olacağını söylüyoruz. Burada aslında çok fazla alternatifimiz yok, ama JWT şemasını kullanacığımızı tanımlıyoruz. Bu metottan hemen sonra çağırdığımız AddJWTBearer() metotu asıl önemli olan kısım. Burada artık uygulamamızda Authentication için JWT şemasının taşınacağını ve bununla ilgili çeşitli ayarların olacağını belirtiyoruz.
TokenValidationParameters ile ValidateLifeTime,ValidateIssuerSigningKey,ValidateAudience,ValidateIssuer gibi özellikler ile JWT’nin neye göre doğrulamadan geçeceğini belirtiyoruz. Mesela oluşturulan JWT’nin 1 gün mü, 30 gün mü; kaç gün geçerli olacağını da belirtiyoruz. Bu zaman doğrulamasının yapılıp yapılmayacağını ValidateLifeTime ile ayarlıyoruz.
Bu doğrulamalardan en önemlisi SigningKey‘in doğrulanması. JWT’nin oluşturulması ve doğrulanması için bir anahtar(string) tanımlıyoruz. Bu anahtar oldukça önemli ve kritik bir bilgi olarak saklanmalı. Bu bilginin ele geçirilmesi durumunda JWT ile yapılan doğrulamalar ciddi bir sıkıntı yaratabilir.
IssuerSigningKey = new SymmetricSecurityKey(key);
ile anahtarımızı(key) oluşturup, JWT bilgisini bu anahtar ile şifreliyoruz.
AddJwtBearer() yani JWT taşıyıcısı tanımlarını oluşturduğumuz bu metotta, aynı zamanda bu taşıyıcı üzerindeki bazı event‘leri de tanımlayabiliyoruz. JWT doğrulandığı zaman ekstra, JWT şeması ile taşınan diğer veriler üzerinden çeşitli doğrulamaları yapabiliyoruz. Mesela aşağıdaki örnekte, şema ile gelen Claim’lerden isim özelliğini alıp, bunu kontrol edebiliriz. (Bu Claim’leri nasıl ekliyoruz biraz sonra oraya da geleceğim.)
Bu Claim’ler ile taşınan ek bilgileri kendi yazacağımız gereksinimler ile doğrulayıp JWT’nin komple doğrulanmasını tamamlamış oluyoruz.
Biraz hızlı oldu belki ama başta da dediğim gibi; hızlı olacak demiştim 🙂 Direk GitHub’a eklediğim örnek projeyi indirip çalıştırıp yazı ile paralel okursanız kodları da çok daha net olacaktır.
Şimdi buraya kadar, uygulamamızda JWT doğrulamasının yapılacağını ve bu doğrulamanın nasıl yapılacağını belirttik. Şimdi belirttiğimiz doğrulama özelliklerine göre JWT oluşturan kısıma ve JWT doğrulaması ile erişilecek/erişilemeyecek kısımlara geçelim.
Senaryomuz için bir Web API projesi demiştik az biraz yukarda. Şimdi bu projemizde LoginController() diye bir Controller oluşturalım ve bunun “HTTP Post” metodu ile JWT oluşturalım.
Önce kodumuza bakalım, sonra aşağıda önemli yerleri üzerinden geçeriz.
Burada SecurityTokenDescriptor sınıfı ile oluşturulacak JWT’nin tanımını yapıyoruz, adından da anlaşılacağı üzere. Audience, Issuer, Expires gibi özellikleri doğrulanma kriterleri olduğu için oldukça önemli. Mesela Expires’da oluşturulan JWT’nin ne kadar geçerli olacağını tanımlıyoruz.
Burada önemli bir kısım da JWT şemasını imzalamak için kullanacağımız anahtar(key) ve imzalamada kullanılacak güvenlik algoritması. Burada bu anahtar doğrulamanın yapılacağı anahtar ile aynı olmalı ki, imzalanan şema doğrulanabilsin. ASP.NET Core ile bir çok SecurityAlgorithms tipi mevcut. Bunlardan ihtiyacımıza uygun derecedekini seçebiliriz.
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
SecurityTokenDescriptor sınıfının Subject özelliği ile ClaimsIdentity()‘yi yani Claims’ları tanımlıyoruz. Burada doğrulama yapılacak kimlik bilgisi özellikleri olarak düşünmek sağlıklı olacaktır. Bu örnekte ben isim ve telefon numarasını Claim olarak ekledim. Oldukça geniş bir Claim tipi var, siz de istediğiniz gibi ekleyebilirsiniz. Az önce yukarıda doğrulama kısmında, birazdan bahsedeceğim dediğim kısım burasıydı. Ek olarak doğrulama yapmak için kullanabileceğimiz değerleri Claim’ler ile taşımak mümkün.
Oluşturduğumuz bu SecurityTokenDescriptor sınıfı ile şimdi JWT’yi yani token’ımızı yaratma zamanı. Bunun için de .CreateToken() metotu ile yaratma işini yapıyoruz. .Write() metodu ile de JWT’yi şemasına göre yazıyoruz.
Bu örnekte token’ı Web API metotundan da dönüyoruz ki, bu metotu kullanarak token’ı client’lara(Diğer web uygulamaları, mobil uygulamalar vs….) ulaştırabilelim. Client’lar diyerek yine aynı GitHub projesinde bu Web API’dan token alan basit bir örnek var. Ona da mutlaka göz atın.
Şimdi buraya kadar nasıl JWT doğrulaması yapılacağını tanımladık. JWT oluşturduk, client’ların alabilmesini sağlayıp onları işaretledik. Peki bu oluşturulan JWT ile işaretlenen client’ların, bu JWT ile kaynaklara erişip erişemeyeceğini nasıl tanımlarız?
En kolay kısım… Ve herkesin bildiği bir özellik. [Authorize] özelliğini istediğimiz sınıf ya da metota ekleyerek, sadece JWT sahibi olan client’ların erişeceğini belirtiyoruz. Aşağıdaki örnekte gerçi tek bir tane GET metodu var ama; burada token bilgisi olmadan bu metotu çalıştırmak mümkün değil. Bu metoda doğrulanmış bir token’a sahip olmayan bir request geldiği zaman, “401 Unauthorizated” sonucu dönecektir.
Peki Login() ile aldığımız bir JWT’yi yani token’ı kullanarak diğer HTTP metotlarını nasıl çağıracağız? diye sorduğunuzu hissediyorum. Yapılan request’lerin HTTP Header‘ında Authorization anahtarı ile Bearer {Token}‘ı göndermek yeterli olacaktır.
ASP.NET Core tarafında bu hızlı anlattıklarımı yapmak gerçekten çok kolay ve hızlı. Sadece bir kaç ekleme ile ASP.NET Core uygulamanızı JWT ile güvenlik doğrulaması yapabilecek şekilde evriltmeniz oldukça çok kolay. Baştada dediğim gibi sadece anahtar kelimeler ve kodlar ile hızlı içerik oluşturup, biraz merak duygunuzu ve biraz da araştırma hissiyatınızı arttırmak istedim. Umarım az da olsa faydalı bir yazı olmuştur.
Farklı senaryolar ile farklı ihtiyaçlarınız olacak. Eğer daha önce duymadınız ya da çalışma fırsatınız olmadıysa OpenId, OAuth konuları karşınıza çıkacak. IdentityServer(4) karşınıza çıkacak. Kısace ASP.NET Core ile bir çok kimlik doğrulama yöntemi uygulamak oldukça kolay ve hızlı. Her türlü soru ve düşüncenizi paylaşmaktan çekinmeyin. Bir sonraki yazıda görüşene kadar, mutlu kodlamalar. 🙂