.Net Core ile JWT Token Kullanımı ve JWT Token Nedir?

JWT (JSON Web Tokens). WebApi ile mobil, IOT veya single page web projelerinde kullanıcı doğrulama, kullanıcı tanıma, veri bütünlüğünü ve bilgi güvenliğini koruma gibi noktalarda kullanılmakta.

Neden JWT ?

  • JSON kullanması
  • URL üzerinde taşınabilmesi
  • Web çerezleri kullanma zorunluluğu olmaması
  • CSRF ataklarına karşı daha kapalı olması
  • Hızlı doğrulama yapılabilmesi
  • Kolay ölçeklenebilir olması
  • Web uygulamaları açısından HTTP session gerekmemesi, stateless kullanıma uygun olması
  • Veri bütünlüğünü sağlaması

JWT Neden Güvenli ?

  • JWT, Base64 olarak şifrelenir
  • HASH256 kripto algoritması kullanılır
  • 3 Bölümden Oluşur

JWT (Json web token) projesi oluşturalım

 dotnet new webapi -o JwtExample 

” code . “ projeyi açalım

Startup.cs Ayarları

services.AddAuthentication(options=>{
                options.DefaultAuthenticateScheme=JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme=JwtBearerDefaults.AuthenticationScheme;
                }
            )
            .AddJwtBearer(options=>{
                options.TokenValidationParameters=new TokenValidationParameters{
                    ValidateAudience=true,                    
                    ValidAudience="muratgozcu.com",
                    ValidateIssuer=true,
                    ValidIssuer="muratgozcu.com",
                    ValidateLifetime=true,
                    ValidateIssuerSigningKey=true,                    
                    IssuerSigningKey=new SymmetricSecurityKey(
                        Encoding.UTF8.GetBytes("ŞİFRELEME"))
                };
            });   

  app.UseAuthentication();

2. Bölüm .Net Core ile Kurumsal Mimari – Mapping ve DBContext Konfigrasyonu

Bir önceki bölümde projenin katmanlarını oluşturduk İncelemek için tıklayınız.
2. Bölümde konumuz mapping ve DBContext konfigrasyonu. Fluent Api ‘den de faydalanacağız bu kısımda.
Core katmanına öncelikle EntityConfigratonMapper.cs adında class oluşturalım.

namespace MuratGOZCU.Core
{
    public abstract partial class EntityConfigratonMapper<T> : IEntityTypeConfiguration<T> where T : class
    {
        public abstract void Configure(EntityTypeBuilder<T> builder);
    }
}

Neden Fluent Desing Pattern ?
Martin Fowler‘ın yayınladığı bir manifestodur. Amacı daha okunabilir ve geliştirilebilir architecture projeler oluşturmak.
Başka bir yazıda fluent design pattern konusunu daha detaylı incelemek üzere serimiz çok uzun olacak konuyu çok da uzatmayalım.

Data katmanına Mapping klasörü oluşturup içerisine CategoryMap.cs oluşturuyoruz.

namespace MuratGOZCU.Data.Mapping
{
    public class CategoryMap : EntityConfigratonMapper<Category>
    {
        public override void Configure(EntityTypeBuilder<Category> builder)
        {
            builder.HasKey(m => m.Id);
            builder.Property(m => m.Name).HasMaxLength(250);
            builder.Property(m => m.Description).HasMaxLength(255);
        }
    }
}

EFDbContext.cs class’ımızı yazalım ve reflection ‘ın güzelliklerinden faydalanarak Entity classlarını yani, Category classımızı ve diğer mapping yapacağımız bütün tabloları database’e migration ile göndereceğiz. Aşağıda generic bir method yazıp EntityConfigratonMapper referansını içeren bütün classları otomatik olarak anlayıp işlemleri gerçekleştirecektir.

namespace MuratGOZCU.Data
{
    public class EFDbContext : DbContext
    {

        public EFDbContext(DbContextOptions<EFDbContext> options)
            : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
                .Where(type => !String.IsNullOrEmpty(type.Namespace))
                .Where(type => type.BaseType != null && type.BaseType.IsGenericType &&
                               type.BaseType.GetGenericTypeDefinition() == typeof(MuratGOZCU.Core.EntityConfigratonMapper<>));
            foreach (var type in typesToRegister)
            {
                dynamic configInstance = Activator.CreateInstance(type);
                modelBuilder.ApplyConfiguration(configInstance);
            }
        }
        
    }
}

appsettings.json dosyasına bir bağlantı dizesi ekleyeceğiz, böylece veritabanıyla etkileşime girebilelim.

"ConnectionStrings": {
  "DefaultConnection": "Server=.;Database=GenericRepository;Trusted_Connection=True;MultipleActiveResultSets=true"
},

Startup.cs Konfigrasyonu


namespace MuratGOZCU.Web
{
    public class Startup
    {
        public IConfigurationRoot Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<EFDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

           
        }
    }
}

Asp.Net Core Docker’da Nasıl Deploy Edilir

Docker containerlerı kullanma amacı sanal veya gerçek sunucu işlemlerine göre daha esnek, taşınabilir ve kolay kopyalama işlemleridir. Diğer bir artısı standart sunucu işlemlerine göre her proje için özel environment’ları kurabiliriz.

Docker container, uygulamanızı ihtiyaca göre farklı makinelerde horizontal bir şekilde çoğaltmanızı oldukça kolaylaştırır. Örneğin, Linux’ta geliştirmeyi tamamladık ve mac makinama gittim ve uygulamanın ordada çalışmasını istiyoruz repoyu klonladım ve “docker-compose-up” yapmam o makinada uygulamayı ve bağımlılıklarını kurup tekrar browserdan kolayca erişebilmeme olanak sağladı, bu oldukça ciddi bir kolaylık.

Container’lar aynı zamanda izolasyonu sağlarlar. Container’larda kullanılan bağımlılıklar ve ayarlar, makinenizde çalışan diğer uygulamaları etkilemez. Bu, dependecny-conflict’lerle karşılaşmamıza engel olur.

Deployment süreçleri docker ile birlikte oldukça hızlı bir hale gelir. Oluşturulan image’ler bir merkezi docker-registry’e atılır ve ihtiyaç duyulduğunda tekrar tekrar rebuild işlemi olmadan registry’den pull edilerek kullanılabilir.

Docker, uygulamaları bir container içerisinde build, deploy ve manage etmeye yarayan open-source olarak geliştirilen bir toolkit dir. Container; hem uygulama kodunu hemde gerekli bağımlılıkları içeren bir yazılım birimi olarak tanımlanabilir ve her bir container birbirinden izole bir şekilde aynı operating system’ı paylaşarak host edilirler. Tek şart bu host operating system Windows yada Linux olsun Docker runtime kurulu olması gerekmektedir.

Bu yazıda en basit haliyle bir asp.net core uygulaması docker kullanarak nasıl host edilir inceleyeceğiz.

1- Installing Docker

İlgili işletim sisteminize göre makinamıza docker kurmamız gerekmekte. Bunun içi aşağıdaki adreslerden faydalanabilirsiniz.

İlgili instruction’ları takip edip kurulum işlemini tamamladıktan sonra docker işletim sisteminiz üzerinde çalışmaya başlayacaktır. Ben windows üzerinde çalıştığımdan docker for windows sürümünü kurdum. Version kontrolü için cmd’de docker –version komutunu çalıştırdığınızda aşağıdaki gibi kurulu olan versiyon bilgisini görüntüleyebilirsiniz. 

1. Bölüm .Net Core ile Kurumsal Mimari Proje Adımları

Entity Framework ile Generic Repository ‘nin avantajları bulunmakta.
Yazılımcının sırtına çok fazla yük vermemek ve projenin daha uzun vadeli olması için tasarlayıcağımız mimaride entity framework ve base işlemler kullanacağız. Ekip veya bireysel olarak hızlı kod ve anlaşılır kod yazma imkanı sunuyor, Oluşturulan projenin senaryosunu göre base metotlar elbette değişebilir.
çok fazla uzatmadan projenin mimarisini birlikte tasarlayalım.

Projede Kullanılacak Teknolojiler
Fluent Api,
AutoMapper,
Entity Framework,
Mapping,
BaseClass,
WebApi,
BaseController,
UnitOfWork,
Repository ve Generic Repository,
Service ve Generic Service


1.Bölüm : Projenin ve katmanlarının oluşturulması
Solution Projesi açalım ve Libraries ve Presentation adında 2 tane klasör oluşturup içlerine aşağıdaki gibi projenin katmanlarını oluşturalım.

Create a new project – Yeni Proje Oluşturuyoruz

Öncelikle BaseClass oluşturuyoruz ve sonrasında oluşturacağımız DBClasslarımıza kalıtım olarak vereceğiz.

namespace MuratGOZCU.Core
{
    public class BaseEntity
    {
        public int Id { get; set; }
    }
}

Core katmanına Domain adında bir klasör açıp içerisine DBClasslarımızı oluşturuyoruz Category classını oluşturalım.

namespace MuratGOZCU.Core.Domain
{
    public class Category:BaseEntity
    {
        public string Name { get; set; }
        public string Description { get; set; }
    }
}

2 – Blazor kurulumu ve proje oluşturma

Blazor kullanmak için Visual Studio 2019 veya .Net Core 3.0 Preview SDK’sı yüklememiz gerekiyor Buradan indirin veya console ekranona aşağıdaki kodu yazın.

dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview5-19227-01

Blazor kurulumunu tamamladık,
Şimdi proje oluşturalım consol’a aşağıdaki gibi yazıyoruz.

dotnet new blazor -o BlazorApp

Aşağıdaki gibi dosya yapısı oluşacaktır. Blazor’un yapısı gereği Server ve Client olmak üzere iki farklı kütüphane de çalışacağız daha sonra



şimdi projemizi studio code ile açalım.
ve kodlarımızı yazalım.



dotnet run veya dotnet watch run komutu ile projeyi ayağa kaldırıp bir test edelim.
dotnet watch run ile çalışmak daha sağlıklı blazor’un bu sorunu nasıl çözeceğini bilmiyorum ama her yapılan değişiklikte projeyi debug etmek gerekiyor bu biraz fazla can sıkıcı oluyor proje geliştirirken. Html’ de yapılan değişikliklerde render işlemi yapılmıyor bu işlemi sürekli debug etmek yerine watch ile çözüyorum
Sizde öyle yapın 🙂 ama kod yazma performansı biraz düşüyor.
http://localhost:5000 ‘den test ediyoruz.

Projede butona tıkladığımızda sayac artıyor ve DOM işlemi gerçekleşiyor.

Bir 2 derste component oluşturacağız ve parametre göndereceğiz.
kolay gelsin.

Entity framework ile nested model update, delete, insert

Seo’ ya uyumlu başlık bulmaya çalışınca ben 🙂

Senaryomuzu kısaca anlatayım.
Bir üst tablomuz Product ve alt tablomuz ProductPrice “Kaydet” butonuna bastığımızda gönderilen model’de product modelimiz update olacaktır ama alt tablomuz olan productPrice tablomuzda update işlemi olmayacaktır. Örneğin productPrice tablosunda kaydet butonuna basmadan önce bir delete, bir update ve insert işlemi yaptık ve kaydet butonuna bastığımızda product ve productPrice tablosunda ki işlemler yapılacaktır.

Örnek Json Model
// var olan productPrice elemanlarınının state durumlarını delete yapıyoruz.
// Henüz silmedik
context.productPrice.Where(w => w.ProductId== myObject.Id).ToList()
 .ForEach(item => context.productPrice.Delete(item));
  
// İkinci aşamada model'den Idsi 0 olmayanlara state durumlarını update olarak tanımlıyoruz.
// Henüz Update etmedik
Model.ProductPrice
  .Where(x => x.Id != 0).ToList()
  .ForEach(order => context.productPrice.Update(order));

context.productPrice.Attach(myObject);
context.productPrice.Update(myObject);
await _unitOfWork.SaveChangesAsync();

Burada Put işlemi içerisine 3 işlem birden yaptırdık. Eskiden uzun uzun sorgular yazardım. Biraz daha düzenli oldu

Farklı domainler için multi tenant middleware kullanımı

Tenant, saas veya store mantığı ile çalışan bir web uygulamamız var diyelim ve yazılan bütün metotlar için appId yi sorgunun sonuna eklemek gerekiyor.Ama biz bu işlemi otomatik hale getireceğiz.
Url bizim appId’mizi verecektir.
Amacımız :
kod temizliği, güvenlik ve esneklik katacaktır uygulamamıza.

Url’den Gelen Tenant ‘ı cache alıyoruz.

protected override Task<TenantContext<AppTenant>> ResolveAsync(HttpContext context)
        {
            TenantContext<AppTenant> tenantContext = null;
            var hostName = context.Request.Host.Value.ToLower();

            var tenant = _tenantService.GetTenantResultByHostName(hostName);

            var appTenant = new AppTenant()
            {
                Id = tenant.tenant.tenantId,
                Host = tenant.tenant.host,
                Name = tenant.tenant.name,
                RequireSSL = tenant.tenant.RequireSSL,
                LanguageId = tenant.tenant.LanguageId
            };

            if (tenant != null)
            {
                tenantContext = new TenantContext<AppTenant>(appTenant);
            }
            return Task.FromResult(tenantContext);
        }


Örneklerle biraz daha açalım konuyu.

1. adımda appId’yi manuel olarak yazacağım ve bir Get işlemi deneyeceğim.

var userList= _userRepository.Table.where(x=>x.AppId == _appId);

2. adımda ise appId’yi middleware de otomatik eklemiş örneği görelim.

var userList= _userRepository.Table;

Aradaki fark şahane dimi.

Bu senaryoda de her tenant’ın kendi domaini var. Proje ayağa kalkerken ayarlar bir defa yüklenecek.
Önce context classımıı açıp içerisine asembly’den faydalanarak bütün veri tabanı entity classlarına appId ekleyip oluşturduğumuz expression’ı HasQueryFilter ile gönderiyoruz.

 private void AddAllConfigurations(ModelBuilder modelBuilder)
{
	foreach (var entityType in modelBuilder.Model.GetEntityTypes())
	{
		ConfigureGlobalFiltersMethodInfo
			.MakeGenericMethod(entityType.ClrType)
			.Invoke(this, new object[] { modelBuilder });
	}
}


private void ConfigureTenantQueryFilter<TEntity>
                       (ModelBuilder modelBuilder) where TEntity : class
        {
            Expression<Func<TEntity, bool>> expression = null;

            if (typeof(IHaveTenant).IsAssignableFrom(typeof(TEntity)))
            {
                expression = e => ((IHaveTenant)e).TenantId == GetTenantId() 
                  || ((IHaveTenant)e).TenantId == null 
                  || ((IHaveTenant)e).TenantId == 0;
            }

            if (expression != null)
            {
                modelBuilder.Entity<TEntity>().HasQueryFilter(expression);
            }
        }

private static readonly MethodInfo ConfigureGlobalFiltersMethodInfo =
            typeof(EfDbContext).GetMethod(nameof(ConfigureTenantQueryFilter),
                BindingFlags.Instance | BindingFlags.NonPublic);

Artık tenant ayarları tamam. Sorgular sadece bulunduğu domain üzerindeki kaynaklara eişecektir.
Daha detaylı bir tenant makalesi ile görüşmek üzere

1 – Blazor NEDİR ?

Geçmişte JavaScript web projelerinde tekel olmuştu. Sonrasında ise geliştiriciler TypeScrip, Angular vb teknolojilerini çıkardılar ama sonuç olarak kodlarımız dönüp dolaşıp javaScript ile kod yazmaya geçiyordu. Ama WebAssembly bu yaklaşımı değiştirdi.

Neden WebAssembly

JavaScript güçlü bir dil ancak dezavantajları vardı. Bazıları TypeScript tarafından düzeltildi amma ve lakin konumuz single page olduğu için aradaki farkı çok fazla gün yüzüne çıkarmayı düşünmüyorum 😀

C# yazılımcıları tarafından React, Angular, Vuejs gibi JavaScript ve TypeScript tabanlı dillere geçiş yapmak ve proje geliştirmek ilk araştırma evreleri ve konuyu anlama Freddy’nin kabusu gibi geliyordu.

Single page sayfalarda artık sadece C# kodlarından faydalanarak web uygulamalar geliştirebiliyoruz. Kod yazma performansı olarak örnek vermek gerekirse projelerimde angular ve vue js sık sık kullanırım blazor un verdiği kod yazma performansını hiç bir platformda yakalamazsınız . Proje geliştirmek inanılmaz hızlı gerçekleşiyor.
Blazor’un En sevdiğim tarafı modelleri eşleştirme gibi bir sorununuz artık kalmıyor. En büyük sıkıntı aslında çözülmüş oluyor bu kısımda. Server ve Client tarafı da aynı modelleri kullandığı için çok daha hızlı geliştirme yapabiliyoruz. Diğer bir güzel yani Blazor geliştiricileri Angular vb. teknolojilerde javascript tabanlı kütüphaneleri eklemek imkansız olabiliyordu ama blazor geliştiricileri bunu çok kolay bir şekilde entegre edebilmemizi sağlamışlar.

Blazor’un Genel Mimarisi Aşağıdaki Gibidir

Aşağıdaki görüntü Chrome’daki bir Blazor uygulamasının önyükleme işlemini göstermektedir. Uygulama Blazor JavaScript ( blazor.js ) içerir. Mono çalışma zamanını ( mono.wasm ) WebAssembly içinde önyüklemek için Mono’nun JavaScript kitaplığını ( mono.js ) kullanır. Daha sonra uygulamanın DLL dosyasını ( WebApplication2.dll ) ve .NET Framework’ün DLL’lerini yükler .

K

.NET CORE 3.0 yeniliklerini inceleyelim.

Windows Desktop Desteği

Artık .net core ile Desktop uygulamları yazılabilecek.
WPF, Windows formları ve WinUI artık açık kaynaklı olacak.

Newtonsoft  Geliyor

Anladığım kadarı ile Microsoft kendi Json reader’ının yavaş olduğunu fark etti. Kendisinden daha performanslı olduğunu kanıtlayan Newtonsoft u default olarak kullanacak.

Index Sınıfı

Index tipi eklendi. Artık int kullanmak zorunda değiliz. Index tipinin amacı tuttuğumuz değere dizinin neresinden başlayacağını atayabiliriz.

Index i1 = 2;  // 2. index den başla
Index i2 = ^3; // 3. adım ilerle
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "2, 5"

Range Sınıfı

Typescript’ten aşina olduğumuz bir kullanım şekli aslında. Range ise iki Index kullanarak dizinin belirli bir kısmını ifade edebileceğimiz bir yapı.
a dizisi içerisinde i1 ve i2 Index leri arasını range ile alabiliyoruz.


int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var slice = a[i1..i2]; // { 3, 4, 5 }

Kaynak