Gereksinimler ve Ön Hazırlık
Bu rehberi uygulamak için modern bir ilişkisel veritabanı yönetim sistemine (RDBMS) ihtiyacınız var. Örneklerimizde PostgreSQL veya MySQL 8.0+ sürümleri ile uyumlu standart SQL sözdizimi kullanılacaktır. Çalışma ortamınızda şu araçların kurulu olduğundan emin olun:
- Veritabanı Sunucusu: PostgreSQL veya MySQL 8.0+.
- Veritabanı İstemcisi: DBeaver, pgAdmin veya MySQL Workbench gibi bir yönetim arayıcı.
- Temel SQL Bilgisi: Tablo oluşturma (CREATE TABLE) ve ilişkisel sorgular (JOIN) hakkında temel bilgi.
Projenin mimarisini oluştururken "En Az Yetki" (Principle of Least Privilege) ilkesini benimseyeceğiz. Bu ilke, bir kullanıcının sadece görevini yerine getirmesi için gereken minimum yetkiye sahip olması gerektiğini savunur.
Adım 1: Veritabanı Şemasını Tasarlama
Yetkilendirme sistemi üç ana tablodan oluşur: Kullanıcılar (users), Roller (roles) ve bu ikisini birbirine bağlayan Yetkiler (permissions). Bu yapı, çoktan çoğa (many-to-many) ilişki kurmamızı sağlar.
CREATE TABLE roles (
id SERIAL PRIMARY KEY,
role_name VARCHAR(50) NOT NULL UNIQUE,
description TEXT
);
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
role_id INTEGER REFERENCES roles(id)
);
CREATE TABLE permissions (
id SERIAL PRIMARY KEY,
permission_name VARCHAR(100) NOT NULL UNIQUE
);
Yukarıdaki SQL kodu, sistemin temel omurgasını oluşturur. roles tablosu rolleri (Admin, Editör, Üye), users tablosu kullanıcıları ve permissions tablosu ise sistemdeki spesifik eylemleri (örneğin: 'post_create', 'user_delete') tanımlar.
Adım 2: Rol ve Yetki İlişkisini Kurma
Rollerin hangi yetkilere sahip olduğunu belirlemek için bir "ara tablo" (junction table) kullanmalıyız. Bu, bir rolün birden fazla yetkiye, bir yetkinin ise birden fazla role atanabilmesini sağlar.
CREATE TABLE role_permissions (
role_id INTEGER REFERENCES roles(id) ON DELETE CASCADE,
permission_id INTEGER REFERENCES permissions(id) ON DELETE CASCADE,
PRIMARY KEY (role_id, permission_id)
);
Bu tablo, veritabanı normalizasyonunda "birleştirme tablosu" olarak adlandırılır. ON DELETE CASCADE kullanımı, bir rol veya yetki silindiğinde, bu ilişkiyi otomatik olarak temizleyerek veritabanı bütünlüğünü korur.
Adım 3: Veri Ekleme ve Rol Atama
Şimdi sistemimize örnek veriler ekleyelim. Bu adım, sistemin nasıl çalıştığını test etmemize olanak tanır.
-- Rolleri tanımla
INSERT INTO roles (role_name) VALUES ('admin'), ('editor'), ('viewer');
-- Yetkileri tanımla
INSERT INTO permissions (permission_name) VALUES ('create_post'), ('edit_post'), ('delete_post');
-- Admin rolüne tüm yetkileri ata
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id FROM roles r, permissions p WHERE r.role_name = 'admin';
Bu sorgularla, admin rolüne sahip bir kullanıcının tüm işlemleri yapabilmesini sağladık. Diğer roller için benzer şekilde INSERT işlemleri gerçekleştirerek yetki matrisinizi genişletebilirsiniz.
Adım 4: Kullanıcı Yetkilerini Sorgulama
Uygulama tarafında bir kullanıcının belirli bir işlemi yapıp yapamayacağını kontrol etmek için JOIN sorguları kullanırız. Bu, yetkilendirme sisteminin kalbidir.
SELECT p.permission_name
FROM permissions p
JOIN role_permissions rp ON p.id = rp.permission_id
JOIN roles r ON r.id = rp.role_id
JOIN users u ON u.role_id = r.id
WHERE u.username = 'ahmet_yilmaz';
Bu sorgu, 'ahmet_yilmaz' kullanıcısının sahip olduğu tüm yetkileri listeler. Uygulamanızda bu sorgudan dönen sonuç kümesini kontrol ederek, kullanıcının bir butonu görüp görmeyeceğine veya bir API isteğine izin verip vermeyeceğinize karar verebilirsiniz.
Adım 5: Güvenlik ve Performans Optimizasyonu
Yetkilendirme sorguları her sayfada çalışacağı için performans kritiktir. role_id ve permission_id sütunlarına indeks ekleyerek sorgu süresini düşürebilirsiniz.
CREATE INDEX idx_user_role ON users(role_id);
CREATE INDEX idx_role_permissions_role ON role_permissions(role_id);
Kritik Uyarı: Kullanıcı şifrelerini asla düz metin (plain-text) olarak saklamayın. Daima Argon2 veya Bcrypt gibi güçlü hash algoritmaları kullanın. Ayrıca, veritabanı sorgularınızda her zaman "Prepared Statements" kullanarak SQL Injection saldırılarını engelleyin.
Karşılaştırma: Yetkilendirme Yöntemleri
| Yöntem | Avantaj | Dezavantaj |
|---|---|---|
| Veritabanı Bazlı (RBAC) | Merkezi yönetim, esnek | Sorgu yükü |
| Kod Bazlı (Hardcoded) | Hızlı, basit | Değişim zor, yönetilemez |
| JWT (Token) Bazlı | Dağıtık sistemler için uygun | Token iptali zor |
Sıkça Sorulan Sorular
Bir kullanıcıya birden fazla rol atayabilir miyim?
Bu makaledeki örnek basit bir RBAC yapısıdır. Çoklu rol atamak istiyorsanız, users tablosundaki role_id sütununu kaldırıp, user_roles adında yeni bir ara tablo oluşturarak kullanıcı-rol ilişkisini çoktan çoğa çevirmelisiniz.
Yetki değişiklikleri ne kadar sürede yansır?
Veritabanı tabanlı sistemlerde değişiklikler anlıktır. Ancak, performans için yetki bilgilerini önbellekte (Redis vb.) tutuyorsanız, yetki güncellemesi yapıldığında önbelleği de temizlemeyi unutmamalısınız.
SQL Injection'dan nasıl korunurum?
Kullanıcıdan gelen girdileri asla doğrudan sorguya eklemeyin. Kullandığınız programlama dilinin (PHP, Python, Node.js vb.) sunduğu PDO veya ORM kütüphanelerini kullanarak parametreli sorgular (prepared statements) yazın.
Admin rolünü nasıl korumalıyım?
Admin yetkilerini veritabanında 'hard-code' olarak tutmak yerine, uygulama tarafında bir yapılandırma dosyası ile kontrol edebilir veya admin işlemlerini sadece belirli bir IP adresinden erişilebilir kılabilirsiniz.
Veritabanı performansı düşerse ne yapmalıyım?
Sık kullanılan yetki sorgularını veritabanı seviyesinde "View" olarak kaydedebilir veya uygulama tarafında kısa süreli (örn: 5 dakika) önbellekleme mekanizmaları kurabilirsiniz.
Sorumluluk Reddi: Bu rehberdeki kod örnekleri eğitim amaçlıdır. Üretim ortamına almadan önce tüm kodları güvenlik testlerinden geçirmeli ve güncel güvenlik kütüphanelerini kullanmalısınız.
İleri Seviye Senaryo: Dinamik Yetki Denetimi ve Middleware Entegrasyonu
Veritabanı seviyesinde yetki yapısını kurduktan sonra, bu yapıyı uygulama katmanında nasıl tüketeceğiniz kritik bir konudur. Her veritabanı sorgusunda yetki kontrolü yapmak yerine, uygulama tarafında bir Middleware (Ara Yazılım) kullanarak süreci otomatize edebilirsiniz. Aşağıdaki örnek, bir Node.js/Express ortamında veritabanından gelen yetkileri kontrol eden bir yapı mantığını göstermektedir.
Middleware ile Yetki Kontrolü
Kullanıcının veritabanındaki rolleri ile talep edilen işlem arasında bir eşleşme olup olmadığını kontrol eden fonksiyon, uygulamanın güvenlik katmanını oluşturur.
// Örnek: Middleware fonksiyonu
const authorize = (requiredPermission) => {
return async (req, res, next) => {
const userId = req.user.id;
// Veritabanı sorgusu: Kullanıcının yetkileri arasında requiredPermission var mı?
const query = `
SELECT COUNT(*) as count
FROM user_roles ur
JOIN role_permissions rp ON ur.role_id = rp.role_id
JOIN permissions p ON rp.permission_id = p.id
WHERE ur.user_id = ? AND p.name = ?`;
const [result] = await db.execute(query, [userId, requiredPermission]);
if (result[0].count > 0) {
next(); // Yetkili, işleme devam et
} else {
res.status(403).json({ message: "Bu işlem için yetkiniz yok." });
}
};
};
Yetkilendirme Sisteminde Hata Ayıklama ve Loglama
Karmaşık yetki yapılarında, bir kullanıcının neden belirli bir işleme erişemediğini anlamak zor olabilir. Bu durumlar için veritabanı seviyesinde bir Audit Log (Denetim Kaydı) tutmak en iyi pratiktir.
Hata Ayıklama İçin Denetim Tablosu
Her yetki reddi durumunu veya kritik yetki değişimini bir tabloda tutarak, sistemin güvenliğini izlenebilir kılabilirsiniz.
CREATE TABLE access_logs (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
action_attempted VARCHAR(100),
status VARCHAR(20),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Yetki reddedildiğinde loglama prosedürü
DELIMITER //
CREATE PROCEDURE log_access_denied(IN p_user_id INT, IN p_action VARCHAR(100))
BEGIN
INSERT INTO access_logs (user_id, action_attempted, status)
VALUES (p_user_id, p_action, 'DENIED');
END //
DELIMITER ;
İleri İpucu: Yetki Hiyerarşisi
Eğer uygulamanızda "Süper Admin" gibi tüm yetkileri kapsayan roller varsa, bunları kod içerisinde sert kodlamak (hard-code) yerine, veritabanında is_super_admin gibi bir boolean sütunu veya parent_role_id ile bir ağaç yapısı kurarak yönetmek, gelecekteki değişikliklerde sistemin esnekliğini korumanızı sağlar.
Pro İpucu: Veritabanı sorgularınızdaJOINişlemlerini optimize etmek içinuser_idverole_idsütunlarına mutlaka Index ekleyin. Büyük ölçekli sistemlerde index eksikliği, yetki kontrolünün uygulamanın darboğazı haline gelmesine neden olur.
Sonuç
SQL ve veritabanı ile kullanıcı yetki rolü oluşturmak, uygulamanızın güvenliğini profesyonel bir seviyeye taşır. Adım adım ilerleyerek kurduğumuz bu ilişkisel yapı, uygulamanızın ölçeklenmesine ve yeni roller eklenmesine olanak tanır. Bir sonraki adım olarak, bu veritabanı yapısını bir API katmanı ile birleştirerek JWT tabanlı bir kimlik doğrulama sistemi eklemeyi deneyebilirsiniz.


Yorumlar (0)
Yorum Yaz