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

C# İle Token Authentication Uygulanan Web API Tetikleme

Merhaba,

Bir önceki C# İle Web API Tetikleme başlıklı makalemde C# ile Web Api tetiklemeyi incelemiştik. Lakin Token Authentication uygulanan web apinin nasıl tetiklendiğini bu makalemize bırakmıştık. Şimdi bu içeriğimizde, Token Authentication uygulanan web api’den C# ile token talep etmeyi ve bu token ile web api’nin sunduğu hizmetleri kullanmayı inceleyeceğiz.

İçeriğimizde kullanacağımız Web API aşağıdaki gibi olacaktır.

    [Authorize]
    public class PersonelController : ApiController
    {
        NorthwindEntities db = new NorthwindEntities();
        public List<Personel> GetAll() => db.Personeller.OrderByDescending(p => p.PersonelID).Select(p => new Personel
        {
            Adi = p.Adi,
            PersonelID = p.PersonelID,
            SoyAdi = p.SoyAdi,
            Unvan = p.Unvan
        }).ToList();
        public Personel GetSingle(int id) => db.Personeller.FirstOrDefault(p => p.PersonelID == id);
        public IHttpActionResult Post(Personel personel)
        {
            if (personel != null)
            {
                db.Personeller.Add(personel);
                db.SaveChanges();
                return Ok($"{personel.Adi} {personel.SoyAdi} isimli personel başarıyla eklenmiştir.");
            }
            return Ok("Personel eklenirken beklenmeyen bir hatayla karşılaşıldı.");
        }
        public IHttpActionResult Put(Personel personel)
        {
            if (personel != null)
            {
                Personeller oldPersonel = db.Personeller.FirstOrDefault(p => p.PersonelID == personel.PersonelID);
                string oldAdi = oldPersonel.Adi, oldSoyAdi = oldPersonel.SoyAdi;
                if (oldPersonel != null)
                {
                    typeof(Personeller).GetProperties().ToList().ForEach(p =>
                    {
                        if (p.Name != "PersonelID" && (p.Name == "Adi" || p.Name == "SoyAdi" || p.Name == "Unvan"))
                            p.SetValue(oldPersonel, typeof(Personel).GetProperty(p.Name).GetValue(personel));
                    });
                    db.SaveChanges();
                    return Ok($"[{oldAdi} {oldSoyAdi}] isimli personel [{personel.Adi} {personel.SoyAdi}] şeklinde güncellenmiştir. ");
                }
            }
            return Ok("Personel güncellenirken beklenmeyen bir hatayla karşılaşıldı.");
        }
        public IHttpActionResult Delete(int id)
        {
            Personeller personel = db.Personeller.FirstOrDefault(p => p.PersonelID == id);
            if (personel != null)
            {
                db.Personeller.Remove(personel);
                db.SaveChanges();
                return Ok($"{personel.Adi} {personel.SoyAdi} isimli personel başarıyla silinmiştir.");
            }
            return Ok($"Personel silinirken beklenmeyen bir hatayla karşılaşıldı.");
        }
    }

Dikkat ederseniz eğer Web Apimize Authorization uygulanmıştır. Eğer ki Web Apiye Token Authentication’ın nasıl uygulandığı hakkında bir fikriniz yoksa buradaki makalemi a’dan z’ye okumanızı öneririm.

Velhasıl…

Bu Web API’yi kullanacak olan projemizi tasarlarsak eğer öncelikle web apiden gelecek olan verileri karşılayacak entityi oluşturalım.

    class Personel
    {
        public int PersonelID { get; set; }
        public string Adi { get; set; }
        public string SoyAdi { get; set; }
        public string Unvan { get; set; }
    }

Ardından web api’ye erişim sağlayacak servisimizi oluşturalım.

    static class WebApiServices
    {
        static string url = "http://localhost:53156/";
        static string serviceUrl = "";
        static HttpClient client = new HttpClient();
        public static async Task<string> GetAll(string method)
        {
            serviceUrl = $"{url}api/personel/{method}";
            using (HttpResponseMessage response = await client.GetAsync(serviceUrl))
                return await response.Content.ReadAsStringAsync();
        }
        public static async Task<string> GetSingle(string method, int id)
        {
            serviceUrl = $"{url}api/personel/{method}/{id}";
            using (HttpResponseMessage response = await client.GetAsync(serviceUrl))
                return await response.Content.ReadAsStringAsync();
        }
        public static async Task<string> Post<T>(string method, T instance) where T : class, new()
        {
            serviceUrl = $"{url}api/personel/{method}";
            StringContent httpContent = new StringContent(JsonConvert.SerializeObject(instance), Encoding.UTF8, "application/json");
            using (HttpResponseMessage response = await client.PostAsync(serviceUrl, httpContent))
            {
                response.EnsureSuccessStatusCode();
                return await response.Content.ReadAsStringAsync();
            }
        }
        public static async Task<string> Put<T>(string method, T instance) where T : class, new()
        {
            serviceUrl = $"{url}api/personel/{method}";
            StringContent httpContent = new StringContent(JsonConvert.SerializeObject(instance), Encoding.UTF8, "application/json");
            using (HttpResponseMessage response = await client.PutAsync(serviceUrl, httpContent))
            {
                response.EnsureSuccessStatusCode();
                return await response.Content.ReadAsStringAsync();
            }
        }
        public static async Task<string> Delete(string method, int id)
        {
            serviceUrl = $"{url}api/personel/{method}/{id}";
            using (HttpResponseMessage response = await client.DeleteAsync(serviceUrl))
                return await response.Content.ReadAsStringAsync();
        }
    }

Görüldüğü üzere, web api’de ki tüm hizmetleri karşılayacak bir servis oluşturmuş bulunmaktayız. Lakin burada dikkatinizi çekmek istediğim bir husus var ki o da; web api’den token işlemleri gerçekleştiren bir metot ya da çalışmanın daha yapılmamış olmasıdır. Gerekli çalışmayı, gerektiği noktada ilerideki satırlarda icra edeceğiz.

Şimdi, bu oluşturduğumuz servisi personellerle ilgili işleme özel olarak kullanacak olan personel servisini yazalım.

    static class PersonelService
    {
        static string jsonData = "";
        static public List<Personel> GetAllPersoneller()
        {
            jsonData = WebApiServices.GetAll("getall").Result;
            return JsonConvert.DeserializeObject<List<Personel>>(jsonData);
        }
        static public Personel GetPersonel(int id)
        {
            jsonData = WebApiServices.GetSingle("getsingle", id).Result;
            return JsonConvert.DeserializeObject<Personel>(jsonData);
        }
        static public string PostPersonel(Personel personel)
        {
            jsonData = WebApiServices.Post<Personel>("post", personel).Result;
            return JsonConvert.DeserializeObject(jsonData).ToString();
        }
        static public string PutPersonel(Personel personel)
        {
            jsonData = WebApiServices.Put<Personel>("put", personel).Result;
            return JsonConvert.DeserializeObject(jsonData).ToString();
        }
        static public string Delete(int id)
        {
            jsonData = WebApiServices.Delete("delete", id).Result;
            return JsonConvert.DeserializeObject(jsonData).ToString();
        }
    }

Evet… Bu işlemde bittikten sonra artık “PersonelService” sınıfını kullanarak web api’mizi tetikleyebiliriz.

    class Program
    {
        static void Main(string[] args)
        {
            PersonelService.GetAllPersoneller().ToList().ForEach(p => Console.WriteLine($"{p.PersonelID} {p.Adi} {p.SoyAdi}"));
            Console.Read();
        }
    }

Yukarıdaki kod bloğunda görüldüğü üzere rastgele seçtiğim “GetAllPersoneller” isimli metodu çağırmak istediğim zaman aşağıdaki hatayla karşılaşmaktayım.
C# İle Token Authentication Uygulanan Web API Tetikleme
Yani gelen json data;

Authorization has been denied for this request.

mesajını bizlere getirerek web apiye yapılan istek neticesinde yetkimizin yetersiz olduğunu belirtmektedir.

Dolayısıyla bu durumda bir token gerekmektedir. Web api’den token talep edebilmek için önceden oluşturduğumuz “WebApiServices” isimli sınıfa “GetToken” metodunu oluşturalım.

        .
        .
        .
        public static async Task<string> GetToken(string userName, string password)
        {
            serviceUrl = $"{url}token";
            List<KeyValuePair<string, string>> pairs = new List<KeyValuePair<string, string>>
                    {
                        new KeyValuePair<string, string>( "grant_type", "password" ),
                        new KeyValuePair<string, string>( "username", userName ),
                        new KeyValuePair<string, string> ( "Password", password )
                    };
            HttpContent content = new FormUrlEncodedContent(pairs);
            using (HttpResponseMessage response = await client.PostAsync(serviceUrl, content))
                return await response.Content.ReadAsStringAsync();
        }
        .
        .
        .

Evet, bu metot sayesinde web apiden token talep edebilecek ve bu token ile yetki elde edip, web apinin sunduğu tüm nimetleri kullanabileceğiz.

Şimdi bu “GetToken” metodundan gelecek olan token bilgilerini karşılayacak entity classımızı yazalım.

    class Token
    {
        public string access_token { get; set; }
        public string token_type { get; set; }
        public string expires_in { get; set; }
    }

Şimdi “GetToken” metodumuz çalışıyor mu bakalım?
C# İle Token Authentication Uygulanan Web API Tetikleme

Harika… Görüldüğü üzere tokenımızı elde ettik.

Şimdi bu token bilgilerini web apiye yapacağımız taleplerde header olarak kullanalım. Bu işlem için oluşturmuş olduğumuz servislerin fiziksel yapılarına aşağıdaki gibi propertyleri ekleyelim.

    static class WebApiServices
    {
        static string url = "http://localhost:53156/";
        static string serviceUrl = "";
        static HttpClient client = new HttpClient();
        static public Token SetToken
        {
            set
            {
                if (value != null)
                    client.DefaultRequestHeaders.Add("Authorization", "Bearer " + value.access_token);
            }
        }
        .
        .
        .
    }

Bu yöntem ile “SetToken” propertysi tetiklendiğinde “HttpClient” nesnesine bir header eklemiş olacaktır. Dolayısıyla bu nesneyi sınıftaki tüm metotlar kullandığından dolayı header otomatik olarak tüm web api tetiklemelerinde kullanılmış olacaktır.

    static class PersonelService
    {
        static string jsonData = "";
        static public Token SetToken
        {
            set
            {
                WebApiServices.SetToken = value;
            }
        }
        .
        .
        .
    }

Burada ise “WebApiServices” sınıfına ilgili tokenı gönderebilmek için “PersonelService” sınıfında da aynı işlemi uygulamaktayım.

Bu yapmış olduğum yaklaşımla static yapılanmanın faydasından istifade ettiğime dikkatinizi çekerim. Aşağıdaki kod bloğunu incelerseniz eğer;

    class Program
    {
        static void Main(string[] args)
        {
            string jsonToken = WebApiServices.GetToken("Gençay", "12345").Result;
            Token token = JsonConvert.DeserializeObject<Token>(jsonToken);
            PersonelService.SetToken = token;
            Console.Read();
        }
    }

oluşturulan token, static olan “PersonelService” sınıfındaki static “SetToken” propertysini tetikleyecek ve içerisinde static olan “WebApiServices” sınıfındaki static “SetToken” propertysini tetikleyerek gerekli headers eklemesini gerçekleştirecektir. Dolayısıyla bu yöntemi static yapıların; bellekte static bölgede tutulmasından dolayı, heap bölgesine nazaran program yaşam döngüsü boyunca verilen değerleri barındırmaları ve bu değerlere herhangi bir noktadan ulaşabilme imkanı tanımalarından dolayı tercih etmiş bulunmaktayım(bknz : storage kavramı).

Tüm yapıyı bu noktaya kadar inşa ettikten sonra aşağıdaki gibi web apimizi tetikleyelim.

    class Program
    {
        static void Main(string[] args)
        {
            string jsonToken = WebApiServices.GetToken("Gençay", "12345").Result;
            Token token = JsonConvert.DeserializeObject<Token>(jsonToken);
            PersonelService.SetToken = token;
            PersonelService.GetAllPersoneller().ToList().ForEach(p => Console.WriteLine($"{p.PersonelID} {p.Adi} {p.SoyAdi}"));
            Console.WriteLine("*************");
            PersonelService.PostPersonel(new Personel
            {
                Adi = "Gençay",
                SoyAdi = "Yıldız",
                Unvan = ".NET Developer"
            });
            Console.WriteLine("*************");
            PersonelService.GetAllPersoneller().ToList().ForEach(p => Console.WriteLine($"{p.PersonelID} {p.Adi} {p.SoyAdi}"));
            Console.Read();
        }
    }

Bu şekilde projemizi derleyip çalıştırdığımızda sıkıntısız çalıştığını göreceksiniz.

İşte bu noktadan itibaren C# ile Token Authentication Uygulanan Web API’nin nasıl tetiklendiğini detaylı bir şekilde ele almış bulunmaktayız.

Okuması ve takibi zor bir makale olduğundan dolayı zahmet edip okuyan ya da okumayı deneyen tüm emektarlara teşekkür eder, bol bol faydalanılmasını temenni ederim. Her adımı uyguladığınız halde olası hatalarla karşılaşırsanız eğer yorum olarak belirttiğiniz taktirde gerekli istişarelere açık olduğumu belirtmeme gerek olmasa gerek.

Sonraki yazılarımda görüşmek üzere…

İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

3 Cevaplar

  1. Faruk SARI dedi ki:

    Gençay hocam elinize emeğinize sağlık. Her talepte tokenın tekrar tekrar oluşmaması ve geçerlilik kontrolüne göre sadece expire durumunda token ın yenilenmesi için en doğru kullanım öneriniz nedir?

    Ayrıca bu örnekte token get/set işlemini EntitiyServices da değil de WebApiServices otomatikleştirmek uygun olur mu?

    • Gençay dedi ki:

      Merhaba Faruk Hocam;

      Esasında uygulamalarda token üretimi alınan unauthorize hatası üzerine yapılan talep neticesinde gerçekleşmektedir. Yani sizde bir token yoksa token talep edersiniz ve böylece hali hazırda bir token elde edersiniz. Tüm isteklerinizi o token ile gerçekleştirirsiniz, ta ki expire durumunda dolayı token geçersiz olana kadar. Bizler tokenın geçersiz olduğu durumda(yani expire sona erdiğinde) kullanıcıyı tekrar token talebinde bulundurmak yerine Refresh Token dediğimiz stratejiyi kullanmayı tercih ederiz.

      Refresh token, kullanıcıdan alınan token değerinin yanında ekstrada farklı bir değer üretilerek elde edilmektedir. Normal tokenın(access token) süresi n ise refresh tokenın süresini n + m kadar ayarlarız ve access token ile yapılan istek neticesinde bir geçersizlik söz konusu olursa anında refresh token ile yeni bir token üretip kullanıcıya hiç hissettirmeden yoluna devam etmesini sağlarız.

      Bu konuya dair aşağıdaki makalemi incelemenizi tavsiye ederim. Özellikle son paragraflarda refresh token stratejisinin hususi başlık altında değerlendirilip uygulandığına dikkatinizi çekerim.
      https://www.gencayyildiz.com/blog/asp-net-core-3-1-ile-token-bazli-kimlik-dogrulamasi-ve-refresh-token-kullanimijwt/

      Sevgiler hocam…

    • Gençay dedi ki:

      İkinci konuya gelirsek eğer;

      Uygulamada client’ın isteklerini EntityService üzerinden gerçekleştirmekteyiz. Lakin söylediğiniz gibi neden WebApiServices bu sorumluluğu tek başına üstlenmesin ki 🙂

      Tabi ki de uygun olacaktır lakin clientın(main metodu) isteklerden önce ilk olarak elde edilen token değerini WebApiServices’e set etmesi gerekecektir.

      class Program
      {
          static void Main(string[] args)
          {
              string jsonToken = WebApiServices.GetToken("Gençay", "12345").Result;
              Token token = JsonConvert.DeserializeObject<Token>(jsonToken);
              WebApiServices.SetToken = token;
              .
              .
              .
          }
      }
      

      gibi…

      Tekrar sevgiler hocam,

      Kolay gelsin.

Bir cevap yazın

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

*