İçeriğe geç
CılgınYazılım

Güvenlik İleri

Rol Tabanlı Yetkilendirme Paneli

Sıfır bağımlılıklı, OOP mimarili bir yönetim paneli: rol tabanlı yetkilendirme, sunucu taraflı DataTables, CSRF korumalı Ajax, modal CRUD ve açık/koyu tema.

  • PHP 8
  • PDO
  • MySQL
  • Ajax
  • DataTables
  • Bootstrap 5
  • OOP
rol-tabanli-yetkilendirme-paneli/kunye.json
seviye
"İleri"
dosya
94
satir
~20.765
boyut
"4.1 MB"
veritabani
"cy_login"
lisans
"MIT"
inceleme
102
begeni
1
yayin
""

Ekran görüntüleri 6 görsel

Bu örnek ne yapıyor?

  • Rol tabanlı yetkilendirme: hangi rolün neyi yapabildiği tek tabloda, kontrol sunucuda
  • Sıfır bağımlılık — Composer, framework ve CDN yok; SQL dosyasını içe aktarın, çalışır
  • Katmanlı OOP mimari: yönlendirme, denetleyici, model ve görünüm ayrı
  • Sunucu taraflı DataTables ile kullanıcı listesi, arama ve filtreler
  • CSRF korumalı Ajax uçları ve modal içinde tam CRUD
  • Oturum sertleştirme, kaba kuvvet koruması ve parola hashleme
  • Açık/koyu tema ve mobil uyumlu arayüz; kod satır satır Türkçe açıklamalı

Nerede işe yarar?

  • Küçük ve orta ölçekli projelerde hazır bir yönetim paneli temeli
  • Rol ve yetki kavramının gerçek bir uygulama üzerinde öğrenilmesi
  • Var olan bir panele rol tabanlı yetkilendirme eklerken referans alınması
  • Müşteri paneli, bayi paneli veya iç kullanım araçlarının iskeleti
  • Framework kullanmadan katmanlı mimari örneği arayan ekipler

Gereksinimler

  • PHP 8.0+ (pdo_mysql, mbstring, gd) · MySQL 5.7+ veya MariaDB 10.3+ · Apache/mod_rewrite

Nasıl kurulur?

  1. Proje dosyalarını web köküne kopyalayın (örn. XAMPP'ta htdocs veya kendi web sunucunuzun kök dizini)
  2. cy_login.sql dosyasını içe aktarın — tabloları, rolleri ve demo hesapları oluşturur
  3. config klasöründeki veritabanı bilgilerini kendi sunucunuza göre düzenleyin
  4. upload klasörüne yazma izni verin (profil görselleri buraya kaydedilir)
  5. Demo hesabıyla giriş yapın ve rollerin hangi menüleri açtığını karşılaştırın
  6. Canlıya alırken hata gösterimini kapatın ve demo hesapların parolalarını değiştirin

Veritabanı şeması projedeki cy_login.sql dosyasında.

Nasıl çalışıyor?

Bu proje nedir?

PHP öğrenirken en çok takılınan yer, "çalışan kod" ile "doğru kod" arasındaki
farktır. Bu depo, klasik bir CRUD örneğini alıp gerçek bir projede olması
gereken hâline getiriyor: katmanlı mimari, rol tabanlı yetkilendirme,
oturum yönetimi ve baştan sona güvenlik.

Composer yok, framework yok, CDN yok. İndirin, cy_login.sql dosyasını içe
aktarın, çalışır. Tüm kod satır satır Türkçe açıklamalıdır; amaç kopyalamak
değil, neden öyle yazıldığını anlamaktır.

Bu depo daha önce tek dosyalık bir CRUD örneğiydi.
Yeni sürümde tam bir yönetim paneline dönüştürüldü.
Eski kurulumdan geçiş için cy_login.sql dosyasının sonundaki
"6) ESKİ SÜRÜMDEN YÜKSELTME" bölümüne bakın.

Öne çıkan özellikler

Mimari

  • OOP katmanlı yapı — Core / Models / Repositories / Controllers / Views
  • PSR-4 mantığında otomatik yükleyici (Composer'sız)
  • Tek giriş noktası (front controller) + rota tablosu
  • Repository deseni — SQL tek dosyada, denetleyiciler temiz
  • Ara katmanlar (middleware) — yetki kontrolü rota tanımında

Kimlik ve oturum

  • Beni hatırla — iki parçalı anahtar (selector + validator), sır veritabanında

özetlenmiş saklanır, anahtar tek kullanımlıktır ve her otomatik girişte yenilenir

  • Parola değişince tüm cihazlardaki kalıcı oturumlar iptal edilir
  • Kaba kuvvet koruması, oturum sertleştirme, taze yetki kontrolü

Yönetim paneli

  • Herkese açık tanıtım sayfası — kök adres, sistemin ne yaptığını anlatır
  • Roller ve Yetkiler sayfası — yetki matrisi; her kullanıcı kendi sınırlarını görür
  • Role özel kontrol paneli — yönetici/editör istatistik görür, üye kendi hesap özetini
  • Temiz adresler — /login, /users (mod_rewrite kapalıysa tek ayarla eski biçime döner)
  • Açık/koyu tema hesaba bağlıdır — başka cihazdan girseniz de tercihiniz gelir
  • Daraltılabilir sol menü (masaüstünde ikon modu, mobilde çekmece)
  • Kontrol paneli: özet kartlar, 14 günlük kayıt grafiği, işlem günlüğü
  • Kullanıcı yönetimi: sunucu taraflı DataTables, canlı arama, rol/durum filtresi
  • Modal tabanlı ekle / düzenle / detay / sil akışı
  • Hesabım sayfası: profil, görsel, parola değiştirme
  • Sistem sayfası: güvenlik denetimi, veri durumu ve yürürlükteki kurallar

Mobil

  • Off-canvas menü, karartma katmanı, ESC ile kapatma
  • 44×44 px dokunma hedefleri
  • Modallar alttan açılan panel biçiminde
  • Tabloda ikincil sütunlar gizlenir, bilgi ad hücresine taşınır (veri kaybı yok)
  • iOS'ta form alanına dokununca otomatik yakınlaştırma engellenir
  • Çentikli ekranlar için safe-area desteği

Demo hesaplar

RolE-postaParola
Yönetici[email protected]Admin1234
Editör[email protected]Editor1234
Üye[email protected]Uye12345
Örnek 50 kullanıcı[email protected]Demo1234

Giriş ekranındaki demo kartlarına tıklayarak formu tek hamlede doldurabilirsiniz.

⚠️ Canlı sunucuda bu hesapları silin veya parolalarını değiştirin.
Giriş ekranındaki demo kartı bloğunu da kaldırın:
views/auth/login.php içindeki $demoAccounts dizisi ve cy-demo bloğu.

Klasör yapısı

├── index.php                  ← TEK giriş noktası (front controller)
├── .env.example               ← Ortam değişkeni şablonu
├── cy_login.sql               ← Veritabanı kurulum dosyası
│
├── config/
│   └── config.php             ← Tüm ayarlar tek dizide
│
├── routes/
│   └── web.php                ← Rota tablosu + yetki kuralları
│
├── app/
│   ├── Core/                  ← Çatı sınıfları
│   │   ├── Autoloader.php       Sınıf otomatik yükleyici
│   │   ├── Env.php              .env okuyucu
│   │   ├── Config.php           Nokta notasyonlu ayar erişimi
│   │   ├── Database.php         PDO bağlantısı (tembel tekil)
│   │   ├── Session.php          Sertleştirilmiş oturum
│   │   ├── Csrf.php             Sahte istek koruması
│   │   ├── Auth.php             Kimlik doğrulama
│   │   ├── Middleware.php       Rota öncesi kontroller
│   │   ├── Router.php           İstek yönlendirme
│   │   ├── Request.php          Girdi okuma ve temizleme
│   │   ├── Response.php         JSON / yönlendirme / güvenlik başlıkları
│   │   ├── Validator.php        Form doğrulama
│   │   ├── Uploader.php         Güvenli görsel yükleme
│   │   ├── RateLimiter.php      Kaba kuvvet koruması
│   │   ├── Flash.php            Tek seferlik bildirimler
│   │   └── View.php             Şablon motoru
│   │
│   ├── Models/
│   │   ├── User.php           ← Kullanıcı varlığı (entity)
│   │   └── Role.php           ← Roller ve yetki tablosu
│   │
│   ├── Repositories/
│   │   ├── UserRepository.php            ← users tablosunun TEK kapısı
│   │   ├── RememberTokenRepository.php   ← "Beni hatırla" anahtarları
│   │   └── ActivityRepository.php        ← İşlem günlüğü
│   │
│   ├── Http/
│   │   ├── Controller.php
│   │   └── Controllers/
│   │       ├── HomeController.php       ← Herkese açık tanıtım
│   │       ├── AuthController.php
│   │       ├── DashboardController.php
│   │       ├── UserController.php
│   │       ├── RoleController.php       ← Roller ve yetkiler
│   │       ├── ProfileController.php
│   │       ├── SettingsController.php
│   │       └── Api/
│   │           ├── UserApiController.php        ← AJAX CRUD
│   │           └── PreferenceApiController.php  ← Tema tercihi
│   │
│   └── Support/helpers.php    ← e(), url(), can(), icon() …
│
├── views/
│   ├── layouts/               ← admin · auth · public · plain
│   ├── partials/              ← sidebar · topbar
│   ├── home/ auth/ dashboard/ users/ roles/ profile/ settings/ errors/
│
├── assets/
│   ├── css/  cilginyazilim.css (tasarım kalıbı) · admin.css (panel)
│   └── js/   app.js (kabuk) · users.js (CRUD) · login.js
│
├── docs/screenshots/          ← README'deki ekran görüntüleri
└── upload/                    ← Yüklenen görseller (PHP çalıştırma kapalı)

Mimari

İsteğin yolculuğu

Tarayıcı
   │
   ▼
index.php ────────── Autoloader · .env · Config · Session · Güvenlik başlıkları
   │
   ▼
routes/web.php ───── Rota eşleştirme
   │
   ▼
Middleware ───────── auth · csrf · can:users.delete   (başarısızsa DURUR)
   │
   ▼
Controller ───────── Girdiyi al · doğrula · yetkiyi kontrol et
   │
   ▼
Repository ───────── Prepared statement ile veritabanı
   │
   ▼
View / JSON ──────── Ekrana bas veya AJAX yanıtı döndür

Neden bu ayrım?

KatmanSorumlulukİçinde ne olmaz?
ControllerAkışı yönetirSQL, HTML
RepositoryVeritabanı erişimiYetki kararı, HTML
ModelVeri + davranışSQL
ViewEkrana basmaİş mantığı, sorgu

Tüm SQL'in tek dosyada olması, güvenlik denetimini de tek dosyaya indirir.

Adres yapısı

Varsayılan temiz adrestir; kökteki .htaccess bununla birlikte gelir:

/login
/users
/api/users/list

Sunucunuzda mod_rewrite yoksa .env içinde APP_PRETTY_URLS=false yapın:

index.php?r=users
index.php?r=api/users/list

Bağlantılar url() yardımcısıyla ve mutlak üretilir (Router::basePath()),
bu yüzden proje bir alt klasörde (htdocs/rbac-login-system) de, alan adının kökünde
de aynı şekilde çalışır.

.htaccess içinde bilerek RewriteBase yoktur. Alt klasör kurulumunda
RewriteBase / yazmak, isteği yanlışlıkla htdocs kökündeki index.php'ye
gönderir (XAMPP'ta karşılama sayfasına düşersiniz).

Roller ve yetkiler

Yetkiler app/Models/Role.php içinde tek bir tabloda tanımlıdır.
Kodda rol adı değil yetki adı kullanılır:

Auth::can('users.delete')           // ✔ doğru
Auth::user()->role === 'admin'      // ✘ kırılgan
YetkiYöneticiEditörÜye
Kontrol panelini görme✔✔✔
Özet istatistikler✔✔—
Kullanıcı listesi✔✔—
Kullanıcı ekleme✔✔—
Kullanıcı düzenleme✔✔—
Kullanıcı silme✔——
Rol atama✔——
Hesap aktif/pasif✔——
Kendi profili✔✔✔
Sistem sayfası✔——

Ek iş kuralları

Rol tablosunun üstünde, kazaları ve yetki yükseltmeyi önleyen kurallar vardır:

  • Yönetici olmayan biri yönetici hesabını düzenleyemez/silemez
  • Kimse kendi rolünü değiştiremez, kendini pasifleştiremez, kendini silemez
  • Sistemdeki son aktif yönetici silinemez ve pasifleştirilemez
  • Yetkisi olmayan biri form üzerinden role gönderirse değer sessizce yok sayılır

Yeni rol eklemek için Role::ABILITIES dizisine bir satır eklemek ve
cy_login.sql içindeki ENUM değerini genişletmek yeterlidir.


Güvenlik önlemleri

TehditÖnlemDosya
SQL InjectionTüm sorgular prepared statement; sıralama sütunu beyaz listedenUserRepository.php
XSSHer çıktı e() ile kaçışlanır; JS'te .text() kullanılırhelpers.php
XSS (2. hat)Content-Security-Policy; satır içi ` yokResponse.php
CSRFOturum bazlı anahtar, hash_equals ile sabit süreli karşılaştırmaCsrf.php
Kaba kuvvet5 hatalı denemede 15 dakika kilit; sayaç veritabanındaRateLimiter.php
Kullanıcı sayımıTek ve aynı hata mesajı + geçerli bir sahte bcrypt özetiyle oyalama (ölçüldü: her iki yol da ~115 ms)Auth.php
Kalıcı oturum çalınması"Beni hatırla" anahtarı iki parçalıdır; sır veritabanında SHA-256 özeti olarak durur, anahtar tek kullanımlıktır ve her girişte dönerRememberTokenRepository.php
Parola değişiminde artık oturumParola değişince kullanıcının tüm cihazlarındaki kalıcı anahtarlar iptal edilirProfileController.php
Session fixationGirişte ve periyodik olarak oturum kimliği yenilenirSession.php
Session hijackinghttponly + samesite + secure çerez, tarayıcı parmak iziSession.php
Yetki yükseltmeRol/durum alanları yetki olmadan okunmaz; güncellemede beyaz listeUserApiController.php
IDORProfil güncellemede ID formdan değil oturumdan alınırProfileController.php
Mass assignmentUserRepository::update() yalnızca izinli sütunları yazarUserRepository.php
Kötü amaçlı dosyaİçerik doğrulaması (getimagesize), uzantıyı sunucu belirler, görsel yeniden üretilirUploader.php
Path traversalbasename() + realpath() ile klasör dışına çıkış engeliUploader.php
Parola saklamapassword_hash() / bcrypt, girişte otomatik needs_rehashUser.php
Bilgi sızıntısıCanlıda hata detayı gizlenir, X-Powered-By kaldırılırindex.php
ClickjackingX-Frame-Options: DENY, frame-ancestors 'none'Response.php
Kod dosyasına erişimapp/, config/, views/, routes/ için .htaccess engeli.htaccess`

Sistem sayfası (yalnızca yönetici) bu önlemlerin kurulumunuzda aktif olup
olmadığını canlı olarak denetler.


Yeni sayfa nasıl eklenir?

Üç adım:

1 — Denetleyiciyi yazın · app/Http/Controllers/ReportController.php

namespace App\Http\Controllers;

use App\Core\Request;
use App\Http\Controller;

final class ReportController extends Controller
{
    public function index(Request $request): void
    {
        $this->view('reports/index', [
            'title' => 'Raporlar',
            'total' => $this->users()->countAll(),
        ]);
    }
}

2 — Rotayı tanımlayın · routes/web.php

$router->get('reports', ReportController::class, 'index', ['auth', 'can:reports.view']);

3 — Görünümü oluşturun · views/reports/index.php

<div class="cy-card">
    <div class="cy-card__body">Toplam kullanıcı: <?= (int) $total ?></div>
</div>

Menüde görünmesi için views/partials/sidebar.php içindeki $menu dizisine
bir satır, yetkinin tanınması için app/Models/Role.php içine
'reports.view' eklemeniz yeterlidir.


Canlıya alma kontrol listesi

  • [ ] .env içinde APP_DEBUG=false
  • [ ] Demo hesapları silin; kendi yönetici hesabınızı oluşturun
  • [ ] views/auth/login.php içindeki demo kartı bloğunu kaldırın
  • [ ] Güçlü bir veritabanı kullanıcısı tanımlayın (root kullanmayın)
  • [ ] HTTPS sertifikası kurun (oturum çerezi otomatik secure olur)
  • [ ] .env, app/, config/, views/, routes/ tarayıcıdan erişilemez olsun
  • [ ] upload/ klasöründe PHP çalıştırma kapalı olsun
  • [ ] PHP hata günlüğünü (error_log) bir dosyaya yönlendirin
  • [ ] Veritabanı yedeklemesini planlayın
  • [ ] Sistem sayfasındaki denetim listesini kontrol edin — hepsi yeşil olmalı

Nginx kullanıyorsanız

.htaccess dosyaları yok sayılır. Sunucu bloğunuza ekleyin:

location ~ ^/(app|config|views|routes)/ { deny all; return 404; }
location ~ /\.               { deny all; return 404; }
location ~ \.(sql|md|log|ini|bak)$ { deny all; return 404; }
location ^~ /upload/         { location ~ \.php$ { deny all; } }

Sık karşılaşılan sorunlar

"Veritabanına bağlanılamadı" hatası

.env dosyanızdaki DB_USER / DB_PASS değerlerini kontrol edin.
XAMPP varsayılanı root ve boş paroladır. MySQL servisinin çalıştığından
emin olun.

Giriş yapıyorum ama sürekli giriş ekranına dönüyorum

Oturum klasörü yazılabilir değildir. php.ini içindeki session.save_path
değerini kontrol edin. Ayrıca tarayıcı çerezlerinin açık olduğundan emin olun.

"Çok fazla hatalı deneme" — kendimi kilitledim

DELETE FROM login_attempts;

Süre dolduğunda (varsayılan 15 dakika) kilit kendiliğinden kalkar.

Görsel yükleniyor ama görünmüyor

upload/ klasörünün yazma izni olmalıdır (755). Ayrıca php.ini içindeki
upload_max_filesize ve post_max_size değerleri en az 2 MB olmalıdır.

Türkçe karakterler bozuk görünüyor

Veritabanı ve tablolar utf8mb4_unicode_ci olmalıdır. cy_login.sql bunu
zaten ayarlar; eski bir tabloyu dönüştürmek için:

ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

Sayfa stilsiz görünüyor / JS çalışmıyor

Tarayıcı konsolunu açın. Content-Security-Policy hatası görüyorsanız
kendi eklediğiniz satır içi bir `` veya dış CDN bağlantısı olabilir.
Tüm JavaScript ayrı dosyada olmalıdır. Geçici olarak config/config.php
içinden security.csp_enabled değerini false yapabilirsiniz.

Görseller küçültülmüyor

PHP'nin gd eklentisi kurulu değildir. Zorunlu değildir; eklenti yoksa
görsel olduğu gibi kaydedilir, diğer güvenlik kontrolleri yine çalışır.
XAMPP'ta php.ini içinde extension=gd satırını açın.


Katkı ve lisans

Katkılara açığız. Depoyu çatallayın (fork), bir dal açın, değişikliğinizi
gönderin. Kod yazarken mevcut üslubu koruyun: **Türkçe açıklamalar,
declare(strict_types=1), katman ayrımına saygı.**

MIT Lisansı — dilediğiniz gibi indirip kullanabilir, değiştirebilir,
ticari projelerinizde kullanabilirsiniz.

cilginyazilim.com ile geliştirildi

Faydalı bulduysanız ⭐ vermeyi unutmayın.

Kaynak kod ağaçtan bir dosya seçin

rbac-login-system
Dosya seçilmedi

İncelemek istediğiniz dosyayı ağaçtan seçin.

Güvenlik gereği kaynak dosyalardaki parola, API anahtarı ve benzeri gizli değerler gösterilmeden önce maskelenir (••••••••).

Sık sorulan sorular

Rol tabanlı yetkilendirme (RBAC) nedir?

Yetkinin kullanıcıya değil role bağlandığı modeldir: kullanıcı bir role atanır, rol bir yetki kümesine sahiptir. Yeni bir kullanıcı eklendiğinde tek tek yetki vermek yerine rol seçilir; bir yetki değiştiğinde o roldeki herkes için aynı anda değişir. Bu panelde roller ve yetkiler tek bir tabloda tutulur ve hangi rolün neyi yapabildiği tek ekrandan görülür.

Menüyü gizlemek yetkilendirme için yeterli mi?

Hayır. Arayüzde bir bağlantıyı gizlemek yalnızca görsel bir kolaylıktır; adresi bilen biri doğrudan istek gönderebilir. Bu yüzden yetki kontrolü her istekte sunucu tarafında yapılır ve yetkisiz istek arayüzden bağımsız olarak reddedilir. Menü gizleme, o kontrolün yerine değil üstüne konur.

Composer veya bir framework gerekiyor mu?

Hayır. Proje sıfır bağımlılıkla çalışır: Composer, framework ve CDN kullanılmaz, tüm kütüphaneler proje içindedir. İndirip cy_login.sql dosyasını içe aktarmanız ve veritabanı bilgilerini yazmanız yeterlidir. Paylaşımlı hostinglerde de ek kurulum gerektirmez.

Parolalar nasıl saklanıyor?

Parolalar hiçbir zaman düz metin olarak tutulmaz; PHP'nin password_hash fonksiyonuyla hashlenir ve doğrulama password_verify ile yapılır. Ayrıca ard arda başarısız giriş denemelerine karşı kaba kuvvet koruması ve sertleştirilmiş oturum ayarları vardır.

Yeni bir sayfa nasıl eklenir?

README'deki "Yeni sayfa nasıl eklenir?" bölümü adım adım anlatır: rota tanımlanır, denetleyici metodu yazılır, görünüm dosyası eklenir ve sayfanın gerektirdiği yetki rol tablosuna işlenir. Yetki tanımlanmadığı sürece sayfa hiçbir rolde görünmez — varsayılan davranış reddetmektir.

Bu örneğin daha gelişmişi projenizde mi lazım?

İhtiyacınızı anlatın; bu yapıyı sizin sisteminize uyarlayalım ya da size özel geliştirelim.

Projenizi Anlatın

Yorumlar

Henüz yorum yok. Takıldığınız bir yer varsa ilk soruyu siz sorun.

Soru sorun veya yorum yazın

Yorumunuz onaylandıktan sonra yayınlanır. Teknik sorularınıza ekibimiz yanıt verir.

Yayınlanmaz; yalnızca gerektiğinde size ulaşmak için.

En az 10 karakter.

İlgili kod örnekleri

İleri

guvenlik/php-rest-api-jwt

JWT ile REST API

  • PHP 8
  • PDO
  • MySQL
  • JWT (HS256)
  • +3

74 inceleme 23 canlı açılış 1 beğeni

Tüm kod örnekleri