JSON Web Token – JWT

19 Haz

JSON Web Token OAuth protokolü üzerinde veri değiş tokuşu sırasında kullanılan JSON formatında veriler içeren veri yapılarıdır.

JSON Web Token, Web içerik kaynağına erişmek için kullanılan bir bilet gibi düşünülebilir. Erişilmek istenen Web kaynağı, bir servis veya web sitesi olabilir. Kaynağa erişmek isteyen kullanıcı uygulamanın, doğrulanmış ve yetkili olup olmadığını tanıtan bir kart gibi düşünebiliriz. Genel olarak access token olarak da adlandırılır.

Token Kullanım Amacı

JSON Web Token kullanım amacı güvenliği sağlamaktır. Web Api gibi kaynaklara erişim talepleri mobil, SPA (Single Page Application)  veya Web sitesi gibi uygulamalardan yapılabilmektedir. Sınırlı erişime sahip yada kullanıcı doğrulama gerektiren Web Api kaynağına yapılan her talep(request) bünyesinde bir erişim bileti(access token) bulundurmak zorundadır.

  • Güvenlik amacıyla kullanılan veri yapılarıdır.
    • İçeriğinde issuer ve subject (claims) bilgileri bulunur.
    • Simetrik veya asimetrik imzalar içerir.
    • Kullanım bitiş süresi(expiration time) içerir.
  • Kullanıcı(Client) uygulama, token talebini yapan taraftır.
  • Yayıncı(issuer), kullanıcıya token veren taraftır.
  • Kaynak(resource) token bilgisini kullanan taraftır.
    • Yayıncı(issuer) ile güvenli bir iletişim içerisindedir.

Token Yapısı

encoded-jwt3

Access token şifrelenmiş ve nokta(.) ile ayrılmış üç bölümden oluşur ve her bir bölüm, kendine has bilgiler barındırır. Bunlar:

  1. Header
  2. Payload
  3. Signature şeklindedir

Bir token içeriğinde bulunan bilgiler şifre çözülerek edilerek elde edilebilir. Online olarak bu işlemi gerçekleştirmek için jwt.io kullanılabilir.

legacy-app-auth-5

Bir JWT kendisi ile ilgili bilgileri bünyesindeki ilgili bölümde barındırır.

JWT Header Bölümü: İki kısımdan oluşur

  • Tür bilgisi (Örnek: JWT)
  • Şifreleme algoritma bilgisi (Örnek: HMAC, SHA256)

JWT Payload Bölümü: Bu bölümde uygulamalar için gerekli bilgiler bulunur. JWT Claims olarak da adlandırılır. JWT Claims üç farklı bölüme ayrılmıştır.

1- Registered Claims: Bu bölümde bulunabilecek içerik bilgisi aşağıdaki gibi olmakla beraber hepsinin kullanılması zorunlu değildir.

  • iss“(issuer): Token üreten yayıncı.
  • sub“(subject): Token başlığı.
  • aud“(audience): Token alıcısı.
  • exp“(expiration time): Token bitiş süresi.
  • nbf“(not before): Belirtilen tarihten önce kullanılamaması.
  • iat“(issued at): Token yayınlanma zamanı.
  • jti“(JWT ID): Unique identifier.

2-Public Claims: Bu bölümde tanımlanan bilgilere URI veya URN şeklinde adlandırma yapılarak token gönderen ve alan tarafların aynı ağda olmadığı durumlarda namespace belirteci ile çarpışmalar önlenebilir. Örneğin: https://www.bayramucuncu.com/jwt_claims/is_admin

3-Private Claims: Genelde kurumsal özel ağlarda kullanılan ve token üretici ve tüketicisi arasında kullanılan özel isimlerdir.


{
    "iss": "bayramucuncu.com", //Registered Claim
    "exp": 123456789, //Registered Claim
    "https://bayramucuncu.com/jwt_claims/is_admin": true, // Public Claim
    "username": "bayram", // Private Claim
    "user_id": "7617dfa9-9084-4e1e-8140-e1dc161ac594" // Private Claim
} 

JWT Signature Bölümü:

Bu bölüm JWT için imza niteliğindedir ve üç bölümün şifrelenmiş olarak birleştirilmesinden oluşur.

  1. JWT Header
  2. JWT Payload
  3. Secret

Örneğin HMAC SHA256 algoritması ile şifreleme yapmak için aşağıdaki yol izlenebilir.

  HMACSHA256(
    base64UrlEncode(header) + "." + base64UrlEncode(payload),
    secret
  )

Token imza bölümü gönderenin kim olduğu bilgisini içerir ve bilginin yolda gelirken değiştirilmediğini garanti altına alır.

Token elde etme süreci nasıl işler?

jwt-diagram

Adım-1: İstemci (client)tarafından JWT üreten sunucuya kullanıcı adı ve şifre ile birlikte POST talebi gönderilir.

Adım-2: Eğer kullanıcı bilgileri doğru ise sunucu istemciye bir JWT gönderir. 

Adım-3: Elde edilen JWT bilgisi ile artık istenen kaynaktan veri elde etmek mümkündür. Kullanıcı tarafı, yeni bir talep gönderirken JWT bilgisini talebin HTTP header bölümüne ekler ve talebi kaynağa iletir.

Adım-4: Sunucu JWT kontrolü sonrasında istemciye cevabı geri gönderir.

JWT Kullanmanın Avantajları

  • Kaynak tarafında oturum yönetimi(session management) yoktur.
  • Cookie kulanımına gerek yoktur.
  • Kaynaklar farklı domainlerde olsa bile tek bir token ile birden fazla kaynağa ulaşmak mümkündür. Çünkü JWT üreten sunucu diğer sunuculardan tamamen bağımsızdır ve her kaynak JWT doğrulamasını JWT üreten sunucudan yapar.
  • Mobil uyumludur. iOS, Android, Windows Mobile gibi ortamlarda token kullanarak kaynağa erişim sağlamak mümkündür.
  • Her token kendi bitiş süresine sahiptir ve logout gibi işlemlere gerek yoktur. Süresi bitmiş bir Token ile kaynağa erişim mümkün değildir.
  • Performansı yüksektir.
  • RFC tarafından standartlaştırılmıştır. Standart kodu RFC 7519‘dur.

Kaynaklar:

  • https://jwt.io/introduction/
  • https://jwt.io/
  • https://tools.ietf.org/html/rfc7797
  • http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#RegisteredClaimName
  • https://auth0.com/blog/2014/12/02/using-json-web-tokens-as-api-keys/

Visual Studio – Paste Special JSON

14 Nis

Visual Studio ortamında JSON nesneleriyle haşır neşir olanlara kolaylık sağlayacak bir özelliği hatırlatmak istiyorum. Diyelim ki elinizde bir JSON nesnesi var ve bu JSON nesnesini  temsil eden C# tiplerini oluşturmak istiyorsunuz. Bu durumda oturup tek tek sınıfları yazmanıza gerek yok. Visual Studio bizim yerimize bu işlemi yapıyor. Nasıl mı?

JSON nesnemiz şu şekilde olsun:

{
   "Employee": {
     "Id": 1,
     "Name": "bayram",
     "salary": 100,
     "Department": {
           "Id": 12,
           "Name": "Development"
      }
   }
 }

Bu nesneyi kopyalayıp Visual Studio penceresinden Edit-> Paste Special -> Paste JSON as classes seçeneğini seçelim.

Paste Special JSON
Paste Special JSON

Sonuç:

Paste JSON as Clasess
Paste JSON as Clasess

Umarım faydası dokunur.

JavascriptSerializer MaxLength Değerinin Aşılması

1 Haz

.Net Framework üzerinde veri transfer mekanizmalarını oluştururken attığımız adımlardan biri de serileştirme işlemidir. Serileştirme işlemlerini Binary, XML veya JSON formatlarında yapabilmekteyiz. Bu yazıda, JSON serileştirme işlemini gerçekleştirirken karşılaşılan bir sorun olan varsayılan olarak ayarlanmış maksimum JSON serileştirme değerinin aşıldığı durumlarda alınan hataları bir ölçüde bertaraf etmeyi inceleyeceğiz.

Örnek uygulamaya geçmeden önce .Net Framework ortamında JSON formatında serileştirme işlemi için gerekli olan ekipmanlardan bahsedelim. Bir nesnenin JSON formatında serileştirilmesi(serialization) veya JSON formatındaki bir verinin .Net nesnesine dönüştürülmesi(deserialization) işlemi için kullanılan referens “System.Web.Extensions”. Projemize bu referansı ekledikten sonra artık ihtiyaç duyduğumuz JavascriptSerializer sınıfına ulaşabiliriz.

Örnek uygulamamızda, verinin büyük olmasını amaçladığımız için bir dosyayı okuyarak JSON formatına dönüştüreceğiz.

Dosya okuma işlemi için aşağıdaki gibi basit bir metod oluşturuyoruz.

private static string readFile()
{
     const string targetDataFile = @"D:\data.txt";

     StreamReader reader = new StreamReader(targetDataFile);

     var result = reader.ReadToEnd();

     return result;
}

Bu metoddan elde ettiğimiz veriyi ise JSON dönüşümünü yapmak için bir başka metod içerisinde kullanıyoruz.

private static string getSerializedFile()
{
    var data = readFile();

    var serializer = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };

    var serialized = serializer.Serialize(data);

    return serialized;
}

Serileştirme işlemi JavascriptSerializer sınıfının Serialize() metodu yardımıyla gerçekleştirilmektedir. JavascriptSerializer sınıfı, MaxJsonLength adında bir property barındırmaktadır. Aksi belirtilmediği taktirde bu property değeri 4MB veriye karşılık gelecek şekilde 2097152 olarak alınır. 4MB’tan fazla veriler için serileştirme sırasında hata verecektir. Bu property için buradan bilgi alabilirsiniz.

Hata InvalidOperationException başlıkı olup, içeriği şu şekildedir: Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.

Örnek uygulamamızda MaxJsonLength property değerini int.MaxValue olarak belirledik. Bu da yaklaşık 2GB civarı bir büyüklüktür.

Asp.Net MVC JSON Model Bining

7 May

Asp.Net MVC mimarisinde yer alan model binding konusunu daha önceki yazılarda ele almıştık. Bu yazıda ise model binding işlemini JSON nesneleri ile uygylamaya çalışacağız. Web projelerinde ihtiyaç duyduğumuz  javascript tabanlı sorguları gerçekleştirmek için JSON nesnelerini .NET nesnelerine dönüştürmemiz gerekebilir. Request data olarak JSON nesnelerini POST edebilmeliyiz.

Örnek Uygulama

Örnek uygulama olarak bir ürün arama işlemini JSON formatındaki verileri bir Action metoda POST edeceğiz.

Öncelikli olarak Request ile gelen JSON fromatındaki veriyi deserialize ederek .Net nesnelerine dönüştürecek olan ModelBinder sınıfımızı oluşturmalıyız.

public class JsonModelBinder : IModelBinder
{
     public object BindModel(ControllerContext controllerContext,
                             ModelBindingContext bindingContext)
     {
          if (controllerContext == null)
             throw new ArgumentNullException("controllerContext");

          if (bindingContext == null)
             throw new ArgumentNullException("bindingContext");

          var serializer = new DataContractJsonSerializer(bindingContext.ModelType);
          var deSerialized = serializer.ReadObject(
                             controllerContext.HttpContext.Request.InputStream);

          return deSerialized;
    }

}

DataContractJsonSerializer sınıfının ReadObject metodu Request üzerinden ile gelen veriyi (InputStream), ModelBindingContext sınıfının ModelType parametresine göre uygun .Net tipine dönüştürür.

Şimdi de ModelBindingContext sınıfına gönderilecek olan tipi hazırlamalıyız.

[DataContract]
[ModelBinder(typeof(JsonModelBinder))]
public class JsonProductSearchRequest
{
     [DataMember]
     public string Name { get; set; }
     [DataMember]
     public string Color { get; set; }
}

System.Runtime.Serialization isim alanına dahil olan DataContract ve DataMember attribute tipleri sınıfları ve sınıf üyelerini serileştirilebilir hale getirmek için kullanılırlar. (Yeri gelmişken söylemekte fayda var; Bir tipi serileştirmek JSON, XML veya Binary şekillerde olabilmektedir. Biz örneğimizde JSON serileştirme üzerinde duracağız). JSonProductSearchRequest sınıfımızı, model binding sırasında ihtiyaç duyulan attribute olan ModelBinder ile imzalıyoruz, parametre olarak ise daha önceden hazırladığımız JsonModelBinder tipini gönderiyoruz.

Artık Controller tarafında ihtiyaç duyulan Action Metodları hazırlayabiliriz.

public class ProductController : Controller
{
   [HttpPost]
   public JsonResult JsonSearch(JsonProductSearchRequest request)
   {
       return null;
   }
}

JsonSearch Action metodu, daha önceden hazırladığımız JsonProductSearchRequest tipinde bir parametre almaktadır.

Şimdi hazırladığımız bu metoda fiddler yardımıyla bir istek (request) göndererek durumu incelemeye çalışalım.

Fiddler Json POST
Fiddler Json POST

Fiddler aracında Composer sekmesinden Request Tipini POST olarak seçiyoruz. Sunucudaki uygulama linkimizi girerek Request Body kısmına uygun JSON verisini giriyoruz. Execute butonuyla çalıştırdığımızda kodumuza geçip “break point” ile takip edersek:

Json Model Binder
Json Model Binder

Gönderdiğimiz verinin POST edildiğini rahatlıkla görebiliriz.

Bu şekilde etkili sonuç veren bir çözüme ModelBinding nimetlerinden faydalanarak ulaşmayı başardık. JSON tabanlı arama işlemlerini rahatlıkla bu şekilde gerçekleştirebiliriz. Ben bu örnekte View sayfalarıyla uğraşmamak adına fiddler aracından faydalanmayı uygun gördüm.

Bir sonraki yazıda görüşmek üzere.