SPRING SECURITY VE JWT NEDİR?
"JWT logout edilemez, çünkü stateless'tır." Bu cümle forumlarda, blog yazılarında ve hatta bazı eğitim materyallerinde sıkça karşımıza çıkar. Üstelik devamında genellikle "o yüzden blacklist tutmak JWT felsefesine aykırıdır" gibi bir sonuç gelir. Oysa bu yaklaşım pratikte ciddi güvenlik açıklarına yol açar: çalınan bir token süresi dolana kadar geçerli kalır, kullanıcı şifresini değiştirse bile eski oturum açık kalmaya devam eder. Gerçek şu ki JWT'yi "iptal edilemez" olarak kabul etmek, tasarım kararı değil yanılgıdır.
JWT Aslında Nasıl Çalışır?
JSON Web Token, üç bölümden oluşan imzalı bir veri yapısıdır: header, payload ve signature. Server, kullanıcı giriş yaptığında payload'a kimlik bilgilerini koyar, gizli anahtar (veya RSA private key) ile imzalar ve istemciye gönderir. İstemci sonraki isteklerde bu token'ı `Authorization: Bearer ...` header'ı ile taşır. Sunucu imzayı doğrular ve oturum bilgisini veritabanından sorgulamadan kullanıcıyı tanır.
Bu noktada yanılgı başlar. "Sunucu veritabanına bakmıyor, demek ki token'ı iptal etmenin yolu yok" denir. Hâlbuki sunucunun veritabanına bakıp bakmayacağı bir tasarım tercihidir, JWT'nin doğasından gelen bir kısıtlama değil. Token imzalı olduğu için içerik değiştirilemez; ancak geçerliliğinin sorgulanıp sorgulanmayacağı tamamen size kalmıştır. konunun teknik kaynakları konuya derinlemesine bir bakış sağlar.
"Logout Edilemez" Yanılgısının Kökeni
Yanılgı şuradan doğar: klasik session-based auth'ta logout, sunucudaki session kaydını silmek demektir. JWT'de böyle bir kayıt yoktur, dolayısıyla "silinecek bir şey yok" sonucuna varılır. Ama logout'un anlamı "sunucu kaydını silmek" değil, "bu token bir daha kabul edilmesin" demektir. Bunu sağlamanın birden fazla yolu vardır:
- Kısa expiry + refresh token: Access token'ı 5-15 dakika gibi kısa tutarsınız, refresh token ile yenilersiniz. Logout'ta refresh token'ı iptal edersiniz.
- Token blacklist (deny list): Logout edilen token'ların jti (JWT ID) değerini Redis gibi hızlı bir store'da expiry süresince tutarsınız.
- Token version / tokenVersion claim: Kullanıcının veritabanındaki bir sayaç ile token içindeki sayaç eşleşmiyorsa reddedersiniz.
- Anahtar rotasyonu: Tüm oturumları geçersiz kılmak için imzalama anahtarını döndürürsünüz.
Bu yaklaşımların hepsi JWT'yi "stateful" yapmaz; sadece güvenlik için gerekli minimum state'i tutar. Bu denge doğru kurulduğunda, JWT hem ölçeklenebilir hem de güvenli olur.

Spring Security'de JWT Filter Zinciri
Spring Security 6 ile birlikte filter chain konfigürasyonu lambda-based DSL'e geçti. Tipik bir JWT entegrasyonu şu adımlardan oluşur:
SecurityFilterChainbean'i tanımlanır,csrf().disable()(stateless API için) vesessionManagement().sessionCreationPolicy(STATELESS)ayarlanır.UsernamePasswordAuthenticationFilter'dan önce çalışacak özel birJwtAuthenticationFiltereklenir.- Bu filter,
Authorizationheader'ından token'ı çıkarır, doğrular veSecurityContextHolder'aAuthenticationnesnesi koyar. - Endpoint'lerde
@PreAuthorizeile rol/yetki kontrolü yapılır.
Token üretimi için io.jsonwebtoken:jjwt veya com.auth0:java-jwt kütüphaneleri yaygındır. Spring tarafında spring-boot-starter-oauth2-resource-server ile JWT decoding işini de hazır olarak alabilirsiniz; bu durumda manuel filter yazmaya gerek kalmaz.
Refresh Token Akışı Neden Kritik?
Access token'ı 24 saat geçerli yapmak, çalınması durumunda saldırgana 24 saatlik bir pencere açar. Çözüm, access token'ı kısa (5-15 dk), refresh token'ı uzun (gün/hafta) tutmaktır. Refresh token veritabanında saklanır — yani aslında "stateful" bir parçadır. Bu, JWT'nin "stateless" felsefesine ihanet değildir; iki katmanlı bir mimaridir:
- Access token: stateless, hızlı doğrulanır, kısa ömürlü.
- Refresh token: stateful, iptal edilebilir, uzun ömürlü.
Logout dediğinizde refresh token'ı veritabanından silersiniz. Access token kısa ömürlü olduğu için en fazla birkaç dakika içinde otomatik olarak geçersiz hâle gelir. Bu süre boyunca riski kabul edersiniz; kritik işlemler için (örn. ödeme onayı) ek bir re-authentication adımı koyabilirsiniz.
Token Revocation: Blacklist mi, Allow List mi?
İki yaklaşım vardır. Blacklist (deny list), iptal edilmiş token'ların listesini tutar — kayıt yoksa token geçerli sayılır. Allow list ise tam tersi: geçerli token'ların listesi tutulur, listede yoksa reddedilir. Allow list daha güvenlidir ama JWT'nin stateless avantajını büyük ölçüde yok eder.
Pratikte en yaygın denge şudur: access token saf JWT (stateless), refresh token allow list'te tutulur. Eğer ek bir güvenlik katmanı isterseniz, JWT'ye jti claim'i ekler ve sadece logout/şifre değişikliği gibi olaylarda ilgili jti'leri Redis'e (expiry süresince) koyarsınız. Redis tabanlı bir blacklist sorgusu mikrosaniye düzeyindedir; "veritabanına bakma" maliyetinden korkulduğu kadar büyük değildir.

Pratik Güvenlik Önerileri
JWT'yi production'da kullanırken sıkça atlanan noktalar vardır. Özellikle imza algoritması seçimi kritiktir: none algoritmasının kabul edilmemesi, kütüphanenin header'daki alg değerini körlemesine kullanmaması gerekir. HS256 yerine RS256 (asimetrik) tercih edilirse, doğrulayan servislerin private key'e ihtiyacı kalmaz.
- Token'ı
localStorageyerineHttpOnlycookie'de saklamayı değerlendirin (XSS koruması). - Payload'a hassas veri (şifre, TC kimlik) koymayın — JWT şifrelenmemiştir, sadece imzalanmıştır.
iss,aud,exp,nbfclaim'lerini her zaman doğrulayın.- Clock skew için 30-60 saniye tolerans bırakın.
- Refresh token rotation uygulayın: her yenilemede eski refresh token iptal edilir.
Spring Boot ile uçtan uca bir kimlik doğrulama uygulaması kurmak için Spring Boot eğitimi içeriğinden yararlanabilirsiniz; orada filter chain, JPA entegrasyonu ve servis katmanı bütünleşik şekilde işlenir.
Yanılgıyı Aşmak
JWT, "iptal edilemez token" değildir. JWT, "doğrulaması imza ile yapılabilen, içerisinde claim taşıyan bir veri formatıdır." Bunun ötesindeki her şey — logout, blacklist, refresh — uygulama tasarımına ait kararlardır. "Stateless olduğu için logout yok" demek, aracın doğasını yanlış okumaktır. Doğru soru şudur: "Bu uygulamada hangi güvenlik gereksinimleri var ve JWT'yi bunlara nasıl uyarlarım?" Bu soruyu sorduğunuzda, blacklist'ten kaçmak yerine ne zaman gerektiğini bilirsiniz; refresh token akışını klişe olarak değil bilinçli bir tercih olarak kurarsınız.



