Node.js İle Kullanıcı Yetkilendirme İçin Rol Tabanlı Erişim Denetimi Nasıl Yapılır?

Node.js İle Kullanıcı Yetkilendirme İçin Rol Tabanlı Erişim Denetimi Nasıl Yapılır?
Node.js İle Kullanıcı Yetkilendirme İçin Rol Tabanlı Erişim Denetimi Nasıl Yapılır?

Ön Hazırlık ve Gereksinimler

Uygulamayı geliştirmeye başlamadan önce sisteminizde Node.js (v20 veya üzeri) yüklü olmalıdır. Projenizi başlatmak için bir klasör oluşturun ve gerekli paketleri yükleyin. Bu rehberde kimlik doğrulama için jsonwebtoken ve şifreleme için bcrypt kütüphanelerini kullanacağız.

# Proje klasörünü oluşturun
mkdir rbac-projesi
cd rbac-projesi
npm init -y

# Gerekli paketleri kurun
npm install express jsonwebtoken bcrypt dotenv

Bu paketler, HTTP isteklerini yönetmek, güvenli token üretmek ve şifreleri veritabanında hash'lenmiş (şifrelenmiş) şekilde saklamak için temel yapı taşlarıdır.

Veritabanı Modeli ve Kullanıcı Rolleri

Rol tabanlı erişim denetiminin temeli, kullanıcı modeline bir "role" alanı eklemektir. Kullanıcı veritabanında tutulurken, rol bilgisi bu kullanıcıya hangi yetkilerin tanımlandığını belirler.

// models/user.js örneği
const userSchema = {
  username: String,
  passwordHash: String, // Şifreler asla düz metin saklanmamalıdır
  role: {
    type: String,
    enum: ['admin', 'editor', 'user'], // Sadece izin verilen roller
    default: 'user'
  }
};

Burada enum kullanımı kritiktir; çünkü sisteme tanımlanmamış bir rolün girilmesini engeller. Veritabanı seviyesinde bu kısıtlamayı yapmak, veri bütünlüğünü korur.

JWT İçerisinde Rol Bilgisini Taşıma

Kullanıcı giriş yaptığında, sunucu bir JWT üretir. Güvenlik açısından, kullanıcının rolünü bu token'ın "payload" kısmına eklemek, her istekte veritabanına sorgu atma ihtiyacını azaltır ve performansı artırır.

const jwt = require('jsonwebtoken');

function generateToken(user) {
  return jwt.sign(
    { id: user._id, role: user.role }, // Rol bilgisi token içinde
    process.env.JWT_SECRET,
    { expiresIn: '1h' }
  );
}

Token'ı oluştururken mutlaka güçlü bir JWT_SECRET anahtarı kullanın ve bu anahtarı asla kodunuzda açık şekilde paylaşmayın; .env dosyasında saklayın.

Rol Tabanlı Erişim Denetimi İçin Middleware Yazma

En kritik adım, gelen isteğin kullanıcısının yetkili olup olmadığını kontrol eden bir "middleware" yazmaktır. Bu fonksiyon, rotaya erişilmeden önce çalışır ve rol eşleşmesi yapmazsa işlemi reddeder.

const authorize = (requiredRole) => {
  return (req, res, next) => {
    // req.user, kimlik doğrulama middleware'inden gelmelidir
    if (req.user && req.user.role === requiredRole) {
      next(); // Yetkili, devam et
    } else {
      res.status(403).json({ message: "Erişim reddedildi: Yetkiniz yok." });
    }
  };
};

Bu fonksiyon, yüksek dereceli fonksiyon (higher-order function) mantığıyla çalışır. İhtiyacınız olan rolü parametre olarak alır ve dinamik bir kontrol mekanizması oluşturur.

Rol Tabanlı Erişim Denetimi Yöntemleri Karşılaştırması

Yöntem Avantaj Dezavantaj
Statik Middleware Basit ve hızlı Esnek değil
Dinamik RBAC Ölçeklenebilir Karmaşık yapı
ABAC (Attribute) Çok detaylı Yüksek performans maliyeti

Uygulama: Rotaları Koruma

Şimdi yazdığımız middleware'i Express rotalarımızda kullanalım. Örneğin, sadece adminlerin erişebileceği bir "kullanıcı silme" rotası tanımlayalım.

const express = require('express');
const app = express();

app.delete('/admin/delete-user', authenticateToken, authorize('admin'), (req, res) => {
  // Sadece admin buraya ulaşabilir
  res.send('Kullanıcı başarıyla silindi.');
});

Burada authenticateToken önce kullanıcının giriş yapıp yapmadığını doğrular, ardından authorize('admin') rol kontrolünü gerçekleştirir. Bu zincirleme yapı, güvenliği katmanlı hale getirir.

Güvenlik Uyarısı: Rol bilgilerini hiçbir zaman istemci tarafında (localStorage gibi) güvenli bir veri kaynağı olarak kullanmayın. Rol kontrolü her zaman sunucu tarafında (server-side) yapılmalıdır. İstemci tarafındaki kontroller sadece kullanıcı deneyimini iyileştirmek (butonları gizlemek gibi) içindir.

Sıkça Sorulan Sorular

Roller veritabanında mı yoksa kodda mı tanımlanmalı?

Roller veritabanında tanımlanmalıdır. Bu sayede kodunuzu değiştirmeden veritabanı üzerinden yeni roller ekleyebilir veya mevcut rolleri güncelleyebilirsiniz.

JWT içindeki rol bilgisi değiştirilebilir mi?

JWT'ler dijital olarak imzalanır. Eğer imza (signature) doğruysa, token içeriği değiştirilemez. Bu yüzden güvenli bir gizli anahtar kullanmak hayati önem taşır.

Birden fazla role sahip kullanıcılar için ne yapmalı?

Eğer bir kullanıcı birden fazla role sahipse, rol alanını bir dizi (array) olarak tanımlayın ve middleware içerisinde includes() metodunu kullanarak kontrol yapın.

Middleware'de hata yönetimi nasıl olmalı?

Yetkisiz erişim denemelerinde her zaman 403 (Forbidden) kodu dönün. Hata mesajlarında sistemin yapısı hakkında çok fazla detay vermeyerek bilgi sızıntısını önleyin.

RBAC yerine başka bir yöntem var mı?

Küçük projeler için RBAC yeterlidir. Ancak çok karmaşık yetkilendirme gerektiren durumlarda, kaynak bazlı yetkilendirme (ACL) veya öznitelik tabanlı erişim denetimi (ABAC) tercih edilebilir.

RBAC Sistemlerinde Performans Optimizasyonu ve Önbellekleme

Rol Tabanlı Erişim Denetimi (RBAC) sistemleri, özellikle yüksek trafikli uygulamalarda her istekte veritabanına giderek rol sorgulamak ciddi bir performans darboğazı yaratabilir. JWT içerisinde rol bilgisini saklamak, veritabanı yükünü hafifletse de, rol değişikliklerinin anlık olarak yansıtılmaması gibi bir dezavantajı beraberinde getirir.

Bu durumu optimize etmek için Redis gibi bir bellek içi veri deposu kullanarak rol ve izin bilgilerini önbelleğe alabilirsiniz. Aşağıdaki örnek, bir kullanıcının rol bilgilerini Redis üzerinden nasıl hızlıca sorgulayabileceğinizi göstermektedir:

const redis = require('redis');
const client = redis.createClient();

const checkRoleWithCache = async (userId, requiredRole) => {
  const cacheKey = `user_role:${userId}`;
  
  // Önce önbelleği kontrol et
  const cachedRole = await client.get(cacheKey);
  if (cachedRole) {
    return cachedRole === requiredRole;
  }

  // Önbellekte yoksa veritabanından getir ve önbelleğe al
  const user = await User.findById(userId);
  await client.set(cacheKey, user.role, 'EX', 3600); // 1 saatlik önbellek
  
  return user.role === requiredRole;
};

RBAC Yapılarında Birim ve Entegrasyon Testleri

Yetkilendirme mekanizmaları, uygulamanın en kritik noktalarıdır. Bu nedenle, middleware katmanlarınızın farklı senaryolarda nasıl tepki verdiğini test etmek hayati önem taşır. Supertest ve Jest kullanarak, yetkisiz erişim denemelerini ve doğru rol atamalarını doğrulayan testler yazmalısınız.

Aşağıdaki örnek, bir rotanın sadece 'admin' rolü tarafından erişilebilir olduğunu doğrulayan basit bir test senaryosudur:

const request = require('supertest');
const app = require('../app');

describe('GET /admin/dashboard', () => {
  it('Yetkisiz kullanıcı erişmeye çalıştığında 403 dönmelidir', async () => {
    const res = await request(app)
      .get('/admin/dashboard')
      .set('Authorization', 'Bearer ' + userToken); // Standart kullanıcı token'ı
      
    expect(res.statusCode).toEqual(403);
  });

  it('Admin kullanıcısı eriştiğinde 200 dönmelidir', async () => {
    const res = await request(app)
      .get('/admin/dashboard')
      .set('Authorization', 'Bearer ' + adminToken); // Admin token'ı
      
    expect(res.statusCode).toEqual(200);
  });
});

Bu testler, gelecekte yapacağınız kod değişikliklerinde yetkilendirme mantığınızın bozulmadığından emin olmanızı sağlar. Özellikle middleware içerisinde yapılan güncellemelerin tüm rotaları etkilediği düşünüldüğünde, bu tür bir test kapsamı projenizin sürdürülebilirliği için vazgeçilmezdir.

Sonuç

Node.js ile Rol Tabanlı Erişim Denetimi (RBAC) uygulamak, uygulamanızın güvenliğini profesyonel bir seviyeye taşımanın en etkili yoludur. Middleware yapısını kullanarak rotalarınızı korumak, hem okunabilirliği artırır hem de güvenlik açıklarını minimize eder. Bir sonraki adım olarak, bu yapıyı daha karmaşık hale getirmek için "Permission" (İzin) bazlı yetkilendirme sistemlerini inceleyebilir ve casl gibi kütüphaneleri projenize dahil edebilirsiniz.

Sorumluluk Reddi: Bu makaledeki kod örnekleri eğitim amaçlıdır. Üretim ortamında (production) kullanmadan önce mutlaka güvenlik testlerinden geçirin, bağımlılıklarınızı güncel tutun ve SQL Injection veya XSS gibi saldırılara karşı gerekli filtreleme kütüphanelerini (örneğin helmet, express-validator) projenize ekleyin.

Bu yazıya tepkinizi paylaşın:
Selin Yılmaz

Yaratıcı problem çözme teknikleri üzerine odaklanan bir içerik editörüyüm. Hayatı pratikleştiren tüyolarla okuyucuya zaman kazandırmayı hedefleyen yazılar kurguluyorum.

Yorumlar (0)

Yorum Yaz