API Resiliency — Bölüm 1: Gateway, Authentication & Authorization, Rate Limiting
Modern bir backend sistemi geliştirirken en sık karşılaşılan sorulardan biri şudur: “Uygulamam büyüyünce ne olacak?”
API Resiliency — Bölüm 1: Gateway, Authentication & Authorization, Rate Limiting
Modern bir backend sistemi geliştirirken en sık karşılaşılan sorulardan biri şudur: “Uygulamam büyüyünce ne olacak?”
Tek bir servisin her şeyi hallettigi bir yapı, başlangıçta pratik görünür. Ama zaman içinde bu yapı; güvenlik açıkları, ölçeklenme sorunları ve bakım kabuslarına dönüşebilir. İşte API Resiliency bu noktada devreye girer.
Bu seri boyunca, sıfırdan bir .NET backend mimarisi kurarak aşağıdaki konuları ele alacağız:
Bölüm 1: Gateway, Authentication & Authorization, Rate Limiting
Bu ilk bölümde şunları anlatacağım:
✅ API Gateway nedir ve neden gereklidir ✅ .NET’te YARP ile gateway nasıl kurulur ✅ JWT tabanlı authentication nasıl uygulanır ✅ Rol bazlı authorization nasıl yapılandırılır ✅ Rate limiting ile brute force ve DoS saldırılarına karşı nasıl korunulur
Makale boyunca lokalimde çalıştığım proje ChangeMind üzerinden ilerleyeceğiz. Daha detaylı inceleme için github adresini ziyaret edebilirsiniz. ChangeMind; koçlar, kullanıcılar, paketler ve ödeme işlemlerini yöneten bir fitness koçluk platformudur.
Proje Yapısı
ChangeMind, Clean Architecture prensiplerine göre yapılandırılmıştır:
ChangeMind/
├── src/
│ ├── ChangeMind.Api/ → HTTP entry point, controller'lar
│ ├── ChangeMind.Application/ → Use case'ler, iş mantığı
│ ├── ChangeMind.Domain/ → Entity'ler, enum'lar, kurallar
│ ├── ChangeMind.Infrastructure/ → DB, dış servisler, JWT
│ └── ChangeMind.Gateway/ → API Gateway (YARP)
Bağımlılık yönü şöyledir:
ChangeMind.Api
└── ChangeMind.Application
└── ChangeMind.Infrastructure
└── ChangeMind.Domain
Bölüm 1: API Gateway
Neden Gerekli?
ChangeMind’ın onlarca endpoint’i var: kullanıcı kaydı, koç yönetimi, paket satın alma, ödeme işlemleri. Her birinin kendi kimlik doğrulama kuralları, erişim politikaları ve yönlendirme mantığı var.
Tüm bunları her serviste ayrı ayrı yönetmek hem tekrar (duplication) hem de hata odağı yaratır. API Gateway, tüm bu kesişen endişeleri (cross-cutting concerns) tek bir noktada toplar:
İstemci
│
│ HTTP istekleri
▼
┌──────────────────────────────────┐
│ ChangeMind.Gateway │
│ ┌──────────────────────────┐ │
│ │ JWT Doğrulama │ │
│ │ Policy Kontrolü │ │
│ │ Route Eşleştirme │ │
│ └──────────────────────────┘ │
│ :5000 │
└───────────────┬──────────────────┘
│
▼
┌──────────────────────────────────┐
│ ChangeMind.Api │
│ :5123 (sadece iç ağda açık) │
└──────────────────────────────────┘
Gateway’in üstlendiği sorumluluklar:
Sorumluluk | Açıklama
---------------|--------------------------------------
Routing | İsteği doğru servise yönlendir
Authentication| Token geçerli mi?
Authorization | Bu kullanıcı bu kaynağa erişebilir mi?
Rate Limiting | Saniyede kaç istek geçebilir?
Health Check | Servis ayakta mı?
Hangi Teknoloji?
.NET ekosisteminde iki popüler seçenek var: Ocelot ve YARP. Ben YARP üzerinden gideceğim. YARP: (Yet Another Reverse Proxy)
Microsoft tarafından geliştirilen, .NET 8+ ile tam uyumlu ve aktif olarak geliştirilen bir çözüm.
YARP Kurulumu
dotnet add package Yarp.ReverseProxy
// src/ChangeMind.Gateway/Program.cs
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
var app = builder.Build();
// Middleware sırası kritik!
app.UseAuthentication();
app.UseAuthorization();
app.MapReverseProxy();
app.Run();
Route Konfigürasyonu (appsettings.json)
YARP, route’ları appsettings.json üzerinden okur. Her route için hangi endpoint'in hangi politikayla korunduğunu buradan yönetiyoruz:
// src/ChangeMind.Gateway/appsettings.json
{
"Urls": "http://localhost:5000",
"Jwt": {
"Secret": "your-super-secret-jwt-key-min-32-characters-long!",
"Issuer": "ChangeMind",
"Audience": "ChangeMindApi",
"ExpirationMinutes": 60
},
"ReverseProxy": {
"Routes": {
"auth-login": {
"ClusterId": "changemind-api",
"Match": {
"Path": "/api/auth/login",
"Methods": ["POST"]
}
// AuthorizationPolicy yok → Herkese açık (login endpoint'i)
},
"users-waiting": {
"ClusterId": "changemind-api",
"AuthorizationPolicy": "coach-or-admin",
"Match": {
"Path": "/api/users/waiting",
"Methods": ["GET"]
}
},
"users-list": {
"ClusterId": "changemind-api",
"AuthorizationPolicy": "coach-or-admin",
"Match": {
"Path": "/api/users",
"Methods": ["GET"]
}
},
"payments": {
"ClusterId": "changemind-api",
"AuthorizationPolicy": "auth",
"Match": {
"Path": "/api/payments",
"Methods": ["POST"]
}
}
},
"Clusters": {
"changemind-api": {
"Destinations": {
"destination1": {
"Address": "http://localhost:5123"
}
}
}
}
}
}
📦 Ne Yaptık? (Bölüm 1 Özeti)
- YARP paketini Gateway projesine ekledik
appsettings.jsonüzerinden 4farklı route tanımladık- Her route için hangi policy’nin uygulanacağını belirledik
/healthendpoint'i ile Gateway'in sağlık durumunu kontrol edebilir hale geldik- Artık tüm istekler Gateway’den geçiyor.
Bölüm 2: Authentication
Neden Gerekli?
Authentication, “bu kullanıcı kim?” sorusunu cevaplar. ChangeMind’da üç farklı rol var: Admin, Coach ve User. Her rolün farklı yetkisi var ve sistem bunu token içindeki bilgiye göre anlıyor.
Doğrulanmamış bir isteğin sisteme girmesi; veri sızıntısı, yetkisiz işlem ve kötü niyetli saldırıların kapısını açar.
JWT Nasıl Çalışır?
1. Kullanıcı login → API, imzalı JWT üretir
2. Kullanıcı her istekte → Authorization: Bearer <token> gönderir
3. Gateway → Token'ı doğrular (imza + süre + issuer + audience)
4. Geçerliyse → İsteği API'ye yönlendir
5. Geçersizse → 401 Unauthorized dön
JWT üç parçadan oluşur:
Header.Payload.Signature
eyJhbGciOiJIUzI1NiJ9 → Header (algoritma: HS256)
.eyJzdWIiOiIxMjM0NTY3ODkwIn0 → Payload (claim'ler: userId, email, role)
.SflKxwRJSMeKKF2QT4fwpMeJf36 → Signature (HMAC-SHA256 imzası)
⚠️ Önemli: JWT payload’u yalnızca Base64 ile encode edilmiştir, şifrelenmemiştir. Payload içeriği herkes tarafından okunabilir. Şifre, kredi kartı veya hassas kişisel veri asla token’a konulmamalıdır.
Infrastructure Katmanı: TokenService
Paket kurulumu:
dotnet add package System.IdentityModel.Tokens.Jwt
dotnet add package Microsoft.IdentityModel.Tokens
TokenService, JWT token'ı oluşturur. Token içine userId, email ve role claim'lerini gömer ve secret key ile imzalar:
public class TokenService(JwtSettings jwtSettings) : ITokenService
{
public (string AccessToken, string RefreshToken) GenerateTokens(
Guid userId, string email, UserRole role)
{
var accessToken = GenerateAccessToken(userId, email, role);
var refreshToken = GenerateRefreshToken();
return (accessToken, refreshToken);
}
public string GenerateRefreshToken()
{
var randomNumber = new byte[64];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomNumber);
}
return Convert.ToBase64String(randomNumber);
}
private string GenerateAccessToken(Guid userId, string email, UserRole role)
{
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(jwtSettings.Secret));
var credentials = new SigningCredentials(
key, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, userId.ToString()),
new Claim(ClaimTypes.Email, email),
new Claim(ClaimTypes.Role, role.ToString()) // "Admin" | "Coach" | "User"
};
var token = new JwtSecurityToken(
issuer: jwtSettings.Issuer,
audience: jwtSettings.Audience,
claims: claims,
expires: DateTime.UtcNow.AddMinutes(jwtSettings.AccessTokenExpiryMinutes),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
{
"Jwt": {
"Secret": "this-is-a-super-secret-key-that-must-be-at-least-32-characters-long-change-in-production",
"Issuer": "ChangeMindApi",
"Audience": "ChangeMindApp",
"AccessTokenExpiryMinutes": 15,
"RefreshTokenExpiryDays": 7
}
}
⚠️ Güvenlik Uyarısı: Secret değerini production ortamında appsettings.json'a yazma. Environment variable veya bir secret manager kullan:
# Linux/macOS
export Jwt__Secret="production-secret-key-here"
# Windows PowerShell
$env:Jwt__Secret = "production-secret-key-here"
# Docker
docker run -e Jwt__Secret="production-secret" myapp
🔐 Ne Yaptık? (Bölüm 2 Özeti)
- JWT token’ın nasıl çalıştığını anladık
Bölüm 3: Authorization
Neden Gerekli?
Authentication “kim olduğunu” söyler; Authorization ise “ne yapabileceğini” belirler. ChangeMind’da erişim kuralları şöyle:
İşlem Kim Yapabilir?
------------------------------------------
Koç oluştur Sadece Admin
Tüm kullanıcıları listele Coach veya Admin
Bekleyen kullanıcıları gör Coach veya Admin
Kendi profilini güncelle Herhangi bir doğrulanmış kullanıcı
Kayıt ol Herkese açık
Login Herkese açık
Gateway’de Security Extension’ı
Gateway, token’ı API ile aynı secret key kullanarak doğrular:
public static class SecurityServiceExtensions
{
public static IServiceCollection AddGatewaySecurity(
this IServiceCollection services,
IConfiguration configuration)
{
var jwtSettings = configuration.GetSection("Jwt").Get<JwtSettings>()
?? throw new InvalidOperationException(
"JWT settings not found in Gateway configuration.");
services.AddSingleton(jwtSettings);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(jwtSettings.Secret)),
ValidateIssuer = true,
ValidIssuer = jwtSettings.Issuer,
ValidateAudience = true,
ValidAudience = jwtSettings.Audience,
ValidateLifetime = true,
// Token süresi dolduğunda anında geçersiz say — 0 tolerans
ClockSkew = TimeSpan.Zero
};
});
// Yedek policy'ler (Program.cs'deki ile aynı isimleri koruyun)
services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
options.AddPolicy("CoachOrAdmin", policy => policy.RequireRole("Coach", "Admin"));
});
return services;
}
}
ClockSkew = TimeSpan.Zero neden önemli? Varsayılan değer 5 dakikadır; yani süresi dolmuş bir token 5 dakika daha geçerli sayılır. Bunu sıfıra indirerek token süresi biter bitmez isteği reddediyoruz.
Policy’ler Gateway’den Route’lara Bağlanıyor
appsettings.json'daki AuthorizationPolicy alanı, YARP'a "bu route'a gelen isteği hangi policy ile kontrol et?" diye söyler:
"coaches-create": {
"ClusterId": "changemind-api",
"AuthorizationPolicy": "admin-only", ← Bu satır yeterli!
"Match": {
"Path": "/api/coaches",
"Methods": ["POST"]
}
}
İstek bu route’a geldiğinde YARP otomatik olarak:
Authorization: Bearer <token>header'ını okur- Token’ı doğrular
- Token’daki
roleclaim'iniadmin-onlypolicy'ye göre kontrol eder
Ne Yaptık? (Bölüm 3 Özeti)
- Gateway seviyesinde 3 policy tanımladık:
auth,admin-only,coach-or-admin appsettings.json'da her route'a uygun policy atadıkClockSkew = TimeSpan.Zeroile token süre toleransını kaldırdık
Bölüm 4: Rate Limiting
Neden Gerekli?
Authentication ve authorization, kim olduğunu ve neye erişebileceğini kontrol eder. Ancak geçerli bir token’a sahip biri bile sistemi kasıtlı ya da kasıtsız olarak zorlayabilir:
- Bir saldırgan saniyede yüzlerce login denemesi yaparak şifreler kırmaya çalışabilir (brute force)
- Bir bot saniyede binlerce kayıt isteği göndererek veritabanını şişirebilir (spam)
- Meşru bir kullanıcı bile hatalı bir döngüyle sistemi bunaltabilir (accidental DoS)
Rate limiting, bir IP veya istek kaynağının belirli bir süre içinde kaç istek gönderebileceğini sınırlar. Limit aşılırsa Gateway isteği API’ye iletmeden 429 Too Many Requests ile reddeder.
İstemci (hızlı istek)
│
▼
Gateway: Rate Limiter
├── ✅ Limit içinde → API'ye yönlendir
└── ❌ Limit aşıldı → 429 + Retry-After header → API'ye ulaşmaz
Hangi Teknoloji?
.NET 7'den itibaren Microsoft.AspNetCore.RateLimiting SDK içinde geliyor ekstra NuGet paketi gerekmez. YARP da route konfigürasyonunda RateLimiterPolicy alanını destekler; böylece her route'a farklı bir policy atayabiliriz.
Algoritma Seçimi
Rate limiting için birkaç farklı algoritma vardır. ChangeMind’da endpoint’in tehdit profiline göre üç farklı algoritma kullandım:
Fixed Window (Sabit Pencere)
Zaman: |── 60 sn ──|── 60 sn ──|
İstekler: ●●●●● ●●●
Limit: 5 5
Her 60 saniyelik pencerede en fazla N istek. Pencere sıfırlandığında sayaç da sıfırlanır. Basit ve bellek dostu. Login ve şifre değiştirme gibi brute force hedefli endpoint’ler için idealdir, pencere dolunca kesin dur.
Zayıf noktası: Pencere sonu ve başında arka arkaya 2×N istek gönderilebilir (boundary burst). Bu nedenle genel endpoint’lerde sliding window tercih edilir.
Sliding Window (Kayan Pencere)
Şimdi: t=45s
Pencere: [t-60s, t] → son 60 saniyede kaç istek?
Son 60 saniyede gönderilen toplam istek sayısına bakar; pencere sürekli “kayar”. Boundary burst problemini ortadan kaldırır. Authenticated genel endpoint’ler için kullanılır.
Token Bucket (Jeton Kovası)
Kova: max 30 token
Her 10s: +10 token eklenir (replenish)
Her istek: 1 token tüketir
t=0: [●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●] 30 token
t=1: 10 istek → [●●●●●●●●●●●●●●●●●●●●] 20 token
t=5: 15 istek → [●●●●●] 5 token
t=10: +10 replenish → [●●●●●●●●●●●●●●●] 15 token
Kısa süreli burst’e (ani yoğunluk) izin verir, uzun vadede dengeli bir hız elde edilir. Admin işlemleri gibi nadiren çağrılan ama arada toplu gelebilen endpoint’ler için uygundur.
Policy Tasarımı
Policy | Algoritma | Limit | Pencere | Kullanım
strict | Fixed Window | 5 istek | 60 sn | Login, şifre değiştirme
public | Fixed Window | 20 istek | 60 sn | Kullanıcı kaydı
standard | Sliding Window | 60 istek | 60 sn | Authenticated endpointler
admin | Token Bucket | 30 token, 10/10sn | - | Admin işlemleri
Gateway Extension’ı
namespace ChangeMind.Gateway.Extensions;
public static class RateLimitingServiceExtensions
{
public static IServiceCollection AddGatewayRateLimiting(
this IServiceCollection services,
IConfiguration configuration)
{
var strict = configuration.GetSection("RateLimiting:Strict");
var pub = configuration.GetSection("RateLimiting:Public");
var standard = configuration.GetSection("RateLimiting:Standard");
var admin = configuration.GetSection("RateLimiting:Admin");
services.AddRateLimiter(options =>
{
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
// Limit aşılınca Retry-After header ekle ve anlamlı mesaj döndür
options.OnRejected = async (context, token) =>
{
context.HttpContext.Response.Headers.RetryAfter =
context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter)
? ((int)retryAfter.TotalSeconds).ToString()
: "60";
await context.HttpContext.Response.WriteAsync(
"Too many requests. Please try again later.", token);
};
// Strict — Login, change-password: brute force koruması
options.AddFixedWindowLimiter("strict", opt =>
{
opt.PermitLimit = strict.GetValue("PermitLimit", 5);
opt.Window = TimeSpan.FromSeconds(strict.GetValue("WindowSeconds", 60));
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
opt.QueueLimit = 0;
});
// Public — Register: kayıt spam koruması
options.AddFixedWindowLimiter("public", opt =>
{
opt.PermitLimit = pub.GetValue("PermitLimit", 20);
opt.Window = TimeSpan.FromSeconds(pub.GetValue("WindowSeconds", 60));
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
opt.QueueLimit = 0;
});
// Standard — Authenticated endpoint'ler: boundary burst'e karşı sliding window
options.AddSlidingWindowLimiter("standard", opt =>
{
opt.PermitLimit = standard.GetValue("PermitLimit", 60);
opt.Window = TimeSpan.FromSeconds(standard.GetValue("WindowSeconds", 60));
opt.SegmentsPerWindow = 6; // 60s / 6 = 10s segmentler
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
opt.QueueLimit = 0;
});
// Admin — Admin işlemleri: burst'e izin veren token bucket
options.AddTokenBucketLimiter("admin", opt =>
{
opt.TokenLimit = admin.GetValue("TokenLimit", 30);
opt.ReplenishmentPeriod = TimeSpan.FromSeconds(admin.GetValue("ReplenishmentPeriodSeconds", 10));
opt.TokensPerPeriod = admin.GetValue("TokensPerPeriod", 10);
opt.AutoReplenishment = true;
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
opt.QueueLimit = 0;
});
});
return services;
}
}
Birkaç tasarım kararı:
QueueLimit = 0: Bekleyen istek kuyruğu yok. Limit dolduğunda istek anında reddedilir. Kuyruk açılsaydı istemci 429 yerine gecikmeli 200 alırdı; bu, botların sistemi yavaşça zorlayabileceği anlamına gelir.
SegmentsPerWindow = 6: Sliding window, 60 saniyelik pencereyi 6 adet 10 saniyelik segmente böler. Segment sayısı arttıkça hassasiyet artar, bellek kullanımı da artar. 6 değeri makul bir dengedir.
OnRejected: Retry-After header'ı istemciye "kaç saniye sonra tekrar dene" bilgisini verir. Token bucket için bu değer Lease nesnesinden dinamik hesaplanır; diğerleri için sabit 60 saniye kullanılır.
Configuration (appsettings.json)
Limit değerleri kod içine gömülmemiş — konfigürasyondan okunuyor. Bu sayede production’da yeniden deploy gerekmeden değer ayarlanabilir:
{
"RateLimiting": {
"Strict": { "PermitLimit": 5, "WindowSeconds": 60 },
"Public": { "PermitLimit": 20, "WindowSeconds": 60 },
"Standard": { "PermitLimit": 60, "WindowSeconds": 60 },
"Admin": { "TokenLimit": 30, "ReplenishmentPeriodSeconds": 10, "TokensPerPeriod": 10 }
}
}
Route’lara Policy Atamak
YARP’ın kullanımı çok basit AuthorizationPolicy gibi RateLimiterPolicy da doğrudan route konfigürasyonuna yazılır:
"auth-login": {
"ClusterId": "changemind-api",
"RateLimiterPolicy": "strict", ← 5 istek / 60 sn
"Match": {
"Path": "/api/auth/login",
"Methods": ["POST"]
}
},
"users-create": {
"ClusterId": "changemind-api",
"RateLimiterPolicy": "public", ← 20 istek / 60 sn
"Match": {
"Path": "/api/users",
"Methods": ["POST"]
}
},
"users-waiting": {
"ClusterId": "changemind-api",
"AuthorizationPolicy": "coach-or-admin",
"RateLimiterPolicy": "standard", ← 60 istek / 60 sn (sliding)
"Match": {
"Path": "/api/users/waiting",
"Methods": ["GET"]
}
},
"coaches-create": {
"ClusterId": "changemind-api",
"AuthorizationPolicy": "admin-only",
"RateLimiterPolicy": "admin", ← Token bucket, burst'e izin verir
"Match": {
"Path": "/api/coaches",
"Methods": ["POST"]
}
}
Tüm route → policy eşleşmesi:
Route RateLimiterPolicy
POST /api/auth/login strict
POST /api/users/{id}/change-password strict
POST /api/users (kayıt) public
POST /api/coaches admin
POST /api/coaches/{id}/change-password admin
Diğer tüm authenticated endpointler standard
Middleware Sırası
// src/ChangeMind.Gateway/Program.cs
builder.Services.AddGatewaySecurity(builder.Configuration);
builder.Services.AddGatewayRateLimiting(builder.Configuration); // ← eklendi
// ...
app.UseRateLimiter(); // ← önce rate limit kontrol et
app.UseAuthentication(); // ← sonra token doğrula
app.UseAuthorization(); // ← sonra yetki kontrol et
app.MapReverseProxy();
UseRateLimiter() neden en önce? Rate limiter, isteği API'ye iletmeden önce reddetmeli. Eğer UseAuthentication()'dan sonra koyulsaydı, her reddedilen istek yine de token doğrulaması yükü yaratırdı. Doğru sıra şu maliyeti engeller:
Limit aşıldı → UseRateLimiter() → 429 → İşlem bitti
(token doğrulama + DB sorgusu yok)
⚡ Ne Yaptık? (Bölüm 4 Özeti)
- Ekstra paket kurmadan .NET SDK’nın built-in rate limiter’ını kullandık
- Tehdit profiline göre 4 farklı policy tasarladık:
strict,public,standard,admin - Her YARP route’una
RateLimiterPolicyatadık — tek satır JSON yeterli UseRateLimiter()middleware'ini authentication'dan önce konumlandırdık- Limit değerlerini
appsettings.json'a taşıdık — deploy gerekmeden ayarlanabilir
Genel Mimari Resmi
Bu noktada sistemin tam akışı şöyle:
İstemci
│
│ POST /api/auth/login
▼
┌──────────────────────────────────────┐
│ ChangeMind.Gateway │
│ ┌────────────────────────────────┐ │
│ │ Route: auth-login │ │
│ │ Policy: YOK (herkese açık) │ │
│ └────────────────────────────────┘ │
│ :5000 │
└──────────────┬───────────────────────┘
│ Yönlendir → :5123
▼
┌──────────────────────────────────────┐
│ ChangeMind.Api │
│ AuthController.Login() │
│ → LoginCommandHandler │
│ → TokenService.GenerateTokens() │
│ ← { accessToken, refreshToken } │
└──────────────────────────────────────┘
│
│ { accessToken: "eyJ..." }
▼
İstemci
─────────────────────────────────
Sonraki istek: Korumalı endpoint
─────────────────────────────────
İstemci
│
│ GET /api/users/waiting
│ Authorization: Bearer eyJ...
▼
┌──────────────────────────────────────┐
│ ChangeMind.Gateway │
│ ┌────────────────────────────────┐ │
│ │ Route: users-waiting │ │
│ │ RateLimiterPolicy: standard │ │
│ │ AuthorizationPolicy: coach-or-admin│
│ │ │ │
│ │ 1. Rate limit kontrol │ │
│ │ ❌ Aşıldı → 429 dön │ │
│ │ 2. Token doğrula (imza+süre) │ │
│ │ ❌ Geçersiz → 401 dön │ │
│ │ 3. Role: "Coach" veya "Admin" │ │
│ │ ❌ Yetersiz → 403 dön │ │
│ │ ✅ Geçti → yönlendir │ │
│ └────────────────────────────────┘ │
└──────────────┬───────────────────────┘
│ Yönlendir → :5123
▼
┌──────────────────────────────────────┐
│ ChangeMind.Api │
│ UsersController.GetWaitingUsers() │
└──────────────────────────────────────┘
Güvenlik Kontrol Listesi
Bölümü tamamlamadan önce şunları kontrol et:
- JWT Secret key en az 32 karakter uzunluğunda
- Secret key production’da environment variable veya secret manager’da saklanıyor
ClockSkew = TimeSpan.Zeroayarlı (token süre toleransı yok)app.UseRateLimiter()→app.UseAuthentication()→app.UseAuthorization()şeklinde middleware sırası olmalı- Hassas endpoint’ler Gateway’de policy ile korunuyor
- Login ve şifre değiştirme endpoint’lerinde
strictrate limiting aktif - Rate limit aşımında
Retry-Afterheader dönüyor - Rate limit değerleri
appsettings.json'da, kodda hard-code değil ChangeMind.Api(:5123) dış ağa kapalı, sadece Gateway üzerinden erişilebilir.Cors:AllowedOrigins- Token payload’ında hassas veri yok (şifre, kredi kartı numarası vs.)
Bu yapı sayesinde:
- Tüm güvenlik kuralları tek noktada (
appsettings.json) yönetiliyor - Yeni bir route eklemek sadece birkaç satır JSON
- Token manipülasyonu, kriptografik imza sayesinde mümkün değil
ChangeMind.Apidoğrudan dışarıya açık değil- Brute force ve DoS saldırıları API’ye ulaşmadan Gateway’de engelleniyor
메타데이터
- post_id
- f5a19451d962
- slug
- api-resiliency-bölüm-1-gateway-authentication-authorization-rate-limiting-f5a19451d962
- url
- https://medium.com/@ongguzel/api-resiliency-b%C3%B6l%C3%BCm-1-gateway-authentication-authorization-rate-limiting-f5a19451d962
- canonical_url
- https://medium.com/@ongguzel/api-resiliency-b%C3%B6l%C3%BCm-1-gateway-authentication-authorization-rate-limiting-f5a19451d962
- author_url
- https://medium.com/@ongguzel
- status
- ok
- fetched_at
- 2026-07-31 04:15:11