farkmt2official 1
farkmt2official
emirhanHCL 1
emirhanHCL
Scarlet 1
Scarlet
Best Studio 1
Best Studio
mannaxxx 1
mannaxxx
Agora Metin2 1
Agora Metin2
Hikaye Ekle

[NODE JS] JWT ile API Authentication

Türkiye'de ilk Mobil & PC Aynı Anda Metin2 Oyna. Triarchonline kalıcı ve uzun ömürlü yapısı ile 24 Temmuz'da açılıyor. | 1-99 Mobil Metin2 Triarch HEMEN TIKLA!

nodejs.png


[NODE JS] JWT ile API Authentication​


Önceki konuda JWT token'ın nasıl oluşturulduğunu ve doğrulandığını öğrendik. Şimdi bu bilgiyi gerçek bir Express API'sine entegre edeceğiz. JWT tabanlı kimlik doğrulama, RESTful API'lerin en yaygın güvenlik yöntemidir. Ben birçok projemde bu yapıyı kullandım ve her seferinde bazı yeni dersler çıkardım. Bu konuda o deneyimlerimi paylaşacağım.

JWT ile API authentication'ın temel akışı şöyledir: Kullanıcı email ve şifresiyle /login endpoint'ine istek gönderir. Sunucu şifreyi doğrular, access token ve refresh token oluşturur ve istemciye gönderir. İstemci, korunan endpoint'lere istek atarken Authorization header'ında "Bearer <token>" formatında access token'ı gönderir. Sunucu, middleware katmanında token'ı doğrular ve isteği işler veya reddeder.

Middleware yaklaşımı, tekrar eden doğrulama kodunu ortadan kaldırır. Her korunan route'a ayrı ayrı doğrulama kodu yazmak yerine, tek bir middleware fonksiyonu yazarsınız ve bunu ilgili route'lara eklersiniz. Bu middleware, Authorization header'ından token'ı çıkarır, doğrular ve kullanıcı bilgilerini req.user'a ekler. Böylece route handler'ları doğrudan kullanıcı bilgilerine erişebilir.

Rol tabanlı yetkilendirme (RBAC) de JWT ile güzelce çalışır. Token payload'ına kullanıcının rolünü eklersiniz ve ikinci bir middleware ile rolü kontrol edersiniz. Örneğin admin paneline sadece admin rolüne sahip kullanıcıların erişmesini sağlayabilirsiniz. Bu iki katmanlı yaklaşım (authentication + authorization) güvenliğinizi güçlendirir.

Refresh token mekanizması, kullanıcı deneyimi açısından kritiktir. Access token'ın süresi kısa tutulur (15 dakika gibi) ama kullanıcı her 15 dakikada bir tekrar giriş yapmak zorunda kalmamalıdır. Refresh token, yeni bir access token almak için kullanılır. Refresh token'ın süresi çok daha uzundur (7-30 gün). Access token süresi dolduğunda, istemci refresh token ile /token/yenile endpoint'ine istek atar ve yeni bir access token alır.

Token kara listesi, kullanıcı çıkış yaptığında veya şifresini değiştirdiğinde eski token'ları geçersiz kılmak için kullanılır. JWT doğası gereği stateless'tır, yani sunucu tarafında durum tutmaz. Ama bazen bir token'ı süresinden önce iptal etmeniz gerekir. Bu durumda Redis gibi hızlı bir depoya iptal edilen token'ları kaydedersiniz.

Rate limiting de API güvenliğinin önemli bir parçasıdır. Brute force saldırılarını engellemek için giriş endpoint'ine istek sınırı koymalısınız. Ayrıca token yenileme endpoint'ine de sınır koyarak, çalınan refresh token'ların kötüye kullanımını azaltabilirsiniz.

CORS ayarları da doğru yapılandırılmalıdır. API'niz farklı bir domain'den istek alacaksa, hangi domain'lerin erişebileceğini, hangi header'ların kabul edileceğini belirtmeniz gerekir. Wildcard (*) kullanmak güvenlik riski oluşturur, izin verilen domain'leri açıkça listeleyin.

JavaScript:
// ==============================
// JWT ile Express API Authentication
// ==============================
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');

const uygulama = express();
uygulama.use(express.json());

// Konfigürasyon
const AYARLAR = {
    gizliAnahtar: process.env.JWT_SECRET || 'cok-gizli-anahtar-256-bit-uzunlugunda',
    refreshAnahtari: process.env.JWT_REFRESH_SECRET || 'refresh-gizli-anahtar-256-bit',
    accessSuresi: '15m',
    refreshSuresi: '7d',
    port: 3000
};

// Bellek içi veri depoları (gerçekte veritabanı kullanın)
const kullanicilar = new Map();
const karaListe = new Set(); // İptal edilen tokenlar
const refreshTokenlar = new Map(); // Aktif refresh tokenlar

// ==============================
// Middleware: Token Doğrulama
// ==============================
function kimlikDogrula(req, res, next) {
    const authHeader = req.headers['authorization'];

    if (!authHeader) {
        return res.status(401).json({ hata: 'Authorization header eksik.' });
    }

    // "Bearer TOKEN" formatını ayrıştır
    const parcalar = authHeader.split(' ');
    if (parcalar.length !== 2 || parcalar[0] !== 'Bearer') {
        return res.status(401).json({ hata: 'Geçersiz token formatı. Bearer TOKEN bekleniyor.' });
    }

    const token = parcalar[1];

    // Kara listede mi kontrol et
    if (karaListe.has(token)) {
        return res.status(401).json({ hata: 'Token iptal edilmiş.' });
    }

    try {
        const cozulmus = jwt.verify(token, AYARLAR.gizliAnahtar);
        req.kullanici = cozulmus;
        req.token = token;
        next();
    } catch (hata) {
        if (hata instanceof jwt.TokenExpiredError) {
            return res.status(401).json({ hata: 'Token süresi dolmuş.', kod: 'TOKEN_SURESI_DOLMUS' });
        }
        return res.status(403).json({ hata: 'Geçersiz token.' });
    }
}

// ==============================
// Middleware: Rol Kontrolü
// ==============================
function rolKontrol(...izinliRoller) {
    return (req, res, next) => {
        if (!req.kullanici || !izinliRoller.includes(req.kullanici.rol)) {
            return res.status(403).json({
                hata: 'Bu işlem için yetkiniz yok.',
                gerekliRoller: izinliRoller,
                mevcutRol: req.kullanici?.rol
            });
        }
        next();
    };
}

// ==============================
// Route: Kayıt Ol
// ==============================
uygulama.post('/api/kayit', async (req, res) => {
    try {
        const { kullaniciAdi, email, sifre } = req.body;

        if (!kullaniciAdi || !email || !sifre) {
            return res.status(400).json({ hata: 'Tüm alanlar zorunludur.' });
        }

        if (kullanicilar.has(email)) {
            return res.status(409).json({ hata: 'Bu email zaten kayıtlı.' });
        }

        const sifreHash = await bcrypt.hash(sifre, 10);
        const kullanici = {
            id: Date.now(),
            kullaniciAdi,
            email,
            sifreHash,
            rol: 'kullanici',
            olusturulma: new Date().toISOString()
        };

        kullanicilar.set(email, kullanici);

        res.status(201).json({
            mesaj: 'Kayıt başarılı!',
            kullanici: { id: kullanici.id, kullaniciAdi, email }
        });
    } catch (hata) {
        res.status(500).json({ hata: 'Sunucu hatası.' });
    }
});

// ==============================
// Route: Giriş Yap
// ==============================
uygulama.post('/api/giris', async (req, res) => {
    try {
        const { email, sifre } = req.body;
        const kullanici = kullanicilar.get(email);

        if (!kullanici) {
            await bcrypt.hash(sifre, 10); // Timing attack önlemi
            return res.status(401).json({ hata: 'Email veya şifre hatalı.' });
        }

        const sifreGecerli = await bcrypt.compare(sifre, kullanici.sifreHash);
        if (!sifreGecerli) {
            return res.status(401).json({ hata: 'Email veya şifre hatalı.' });
        }

        // Token oluştur
        const accessToken = jwt.sign(
            { kullaniciId: kullanici.id, kullaniciAdi: kullanici.kullaniciAdi, rol: kullanici.rol },
            AYARLAR.gizliAnahtar,
            { expiresIn: AYARLAR.accessSuresi }
        );

        const refreshToken = jwt.sign(
            { kullaniciId: kullanici.id, tip: 'refresh' },
            AYARLAR.refreshAnahtari,
            { expiresIn: AYARLAR.refreshSuresi }
        );

        refreshTokenlar.set(refreshToken, kullanici.id);

        res.json({
            mesaj: 'Giriş başarılı!',
            accessToken,
            refreshToken,
            kullanici: { id: kullanici.id, kullaniciAdi: kullanici.kullaniciAdi, rol: kullanici.rol }
        });
    } catch (hata) {
        res.status(500).json({ hata: 'Sunucu hatası.' });
    }
});

// ==============================
// Route: Çıkış Yap (Token İptal)
// ==============================
uygulama.post('/api/cikis', kimlikDogrula, (req, res) => {
    karaListe.add(req.token);
    res.json({ mesaj: 'Çıkış başarılı. Token iptal edildi.' });
});

// ==============================
// Korunan Routelar
// ==============================
uygulama.get('/api/profil', kimlikDogrula, (req, res) => {
    res.json({ mesaj: 'Profil bilgileri', kullanici: req.kullanici });
});

uygulama.get('/api/admin/panel', kimlikDogrula, rolKontrol('admin'), (req, res) => {
    res.json({ mesaj: 'Admin paneline hoş geldiniz!', istatistikler: { toplamKullanici: kullanicilar.size } });
});

uygulama.listen(AYARLAR.port, () => {
    console.log(`API sunucusu ${AYARLAR.port} portunda çalışıyor`);
});

JWT ile API authentication, modern web uygulamalarının bel kemiğidir. Middleware tabanlı yaklaşım kodunuzu temiz tutar, rol kontrolü yetkilendirmeyi sağlar ve refresh token mekanizması kullanıcı deneyimini iyileştirir. Güvenlik pratiklerini ihmal etmeyin: HTTPS kullanın, token sürelerini kısa tutun, gizli anahtarlarınızı ortam değişkenlerinde saklayın.
 

Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)

Geri
Üst