Basit Anlatım (Herkes İçin)
Ne yapacağız, neden önemli, kullanıcıya faydası ne?
Ne Yapıyoruz?
Muzibu'da bir kullanıcı telefondan giriş yaptıktan sonra bilgisayardan tekrar giriş yaptığında, telefonundaki oturum otomatik kapanıyor. Şimdi bu olayı kayıt altına alacak bir sistem kuruyoruz.
Günlük Hayattan Örnek: Banka hesabınıza farklı bir cihazdan giriş yapıldığında size SMS/email gelir. "Hesabınıza yeni cihazdan giriş yapıldı: iPhone, İstanbul, 19.12.2025 22:15" gibi. Aynı sistemi Muzibu'ya kuruyoruz!
Neden Önemli?
Güvenlik
Hesabınız çalınırsa kim, ne zaman, nereden giriş yaptı görebilirsiniz. Hırsızı yakalama şansınız artar!
Bildirim
Başka cihazdan giriş yapıldığında email/SMS ile hemen haberdar olursunuz. Gerçek zamanlı uyarı!
Geçmiş
Hangi tarihte hangi cihazlardan giriş yaptığınızı görebilirsiniz. Aktivite geçmişi tamamen sizin kontrolünüzde.
Destek
Müşteri desteğine başvurduğunuzda, hangi cihazdan sorun yaşadığınız kayıtlarda olur. Hızlı çözüm!
Kaydedilecek Bilgiler
Yeni Giriş (Kick Eden)
- 📱 Cihaz: iPhone 13, Chrome
- 🌍 Konum: İstanbul, Türkiye
- 🌐 IP: 185.123.45.67
- 🕐 Saat: 19.12.2025 22:15:30
Eski Oturum (Atılan)
- 💻 Cihaz: Windows 10, Firefox
- 🌍 Konum: Ankara, Türkiye
- 🌐 IP: 78.234.56.89
- 🕐 Son Aktivite: 19.12.2025 22:10:15
Teknik Detaylar (Geliştiriciler İçin)
Database yapısı, implementasyon, teknolojiler
Database Schema: session_termination_logs
CREATE TABLE session_termination_logs (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
-- Kullanıcı Bilgisi
user_id BIGINT UNSIGNED NOT NULL,
user_email VARCHAR(255) NOT NULL,
user_name VARCHAR(255) NOT NULL,
-- Termination Nedeni
termination_reason ENUM('lifo', 'manual', 'timeout', 'logout', 'admin') NOT NULL,
-- YENİ OTURUM (Kick Eden)
new_session_id VARCHAR(255) NOT NULL,
new_login_token VARCHAR(64) NOT NULL,
new_ip_address VARCHAR(45) NOT NULL,
new_user_agent TEXT NOT NULL,
new_device_type VARCHAR(20) NOT NULL, -- desktop, mobile, tablet
new_device_name VARCHAR(100) NOT NULL,
new_browser VARCHAR(50) NOT NULL,
new_platform VARCHAR(50) NOT NULL,
new_country VARCHAR(100) NULL, -- Geolocation
new_city VARCHAR(100) NULL,
new_latitude DECIMAL(10, 8) NULL,
new_longitude DECIMAL(11, 8) NULL,
new_fingerprint VARCHAR(64) NULL, -- Browser fingerprint
-- ESKİ OTURUM (Atılan)
old_session_id VARCHAR(255) NOT NULL,
old_login_token VARCHAR(64) NULL,
old_ip_address VARCHAR(45) NULL,
old_user_agent TEXT NULL,
old_device_type VARCHAR(20) NULL,
old_device_name VARCHAR(100) NULL,
old_browser VARCHAR(50) NULL,
old_platform VARCHAR(50) NULL,
old_country VARCHAR(100) NULL,
old_city VARCHAR(100) NULL,
old_last_activity TIMESTAMP NULL,
-- Meta Bilgiler
notification_sent BOOLEAN DEFAULT FALSE,
notification_method VARCHAR(20) NULL, -- email, sms, push
notification_sent_at TIMESTAMP NULL,
admin_viewed BOOLEAN DEFAULT FALSE,
admin_viewed_at TIMESTAMP NULL,
admin_viewed_by BIGINT UNSIGNED NULL,
-- Timestamps
terminated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_at TIMESTAMP NULL,
updated_at TIMESTAMP NULL,
-- Indexes
INDEX idx_user_id (user_id),
INDEX idx_terminated_at (terminated_at),
INDEX idx_termination_reason (termination_reason),
INDEX idx_notification_sent (notification_sent),
-- Foreign Keys
CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
CONSTRAINT fk_admin_viewer FOREIGN KEY (admin_viewed_by) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Termination Reasons (Kapanma Nedenleri)
LIFO (Last In, First Out)
Yeni cihazdan giriş yapıldı, en eski oturum otomatik kapandı. En yaygın senaryo.
Manual (Kullanıcı Kapattı)
Kullanıcı "Diğer Cihazlardan Çıkış Yap" butonuna bastı. Bilinçli aksiyon.
Timeout (Zaman Aşımı)
60 dakika aktivite yoksa session DB'den silinir. Otomatik temizlik.
Logout (Normal Çıkış)
Kullanıcı "Çıkış Yap" butonuna bastı. Temiz sonlanma.
Admin (Yönetici Müdahalesi)
Admin şüpheli aktivite tespit edip kullanıcının session'ını kapattı. Güvenlik önlemi.
DeviceService.php - Log Kaydetme
// DeviceService.php:468 - enforceDeviceLimit() metoduna eklenecek
protected function enforceDeviceLimit(User $user, string $currentSessionId): void
{
$limit = $this->getDeviceLimit($user);
$sessions = DB::table($this->table)
->where('user_id', $user->id)
->orderBy('last_activity', 'asc')
->get();
$count = $sessions->count();
if ($count > $limit) {
$toRemove = $count - $limit;
$oldSessions = $sessions
->filter(fn($s) => $s->session_id !== $currentSessionId)
->take($toRemove);
// Yeni session bilgisi
$newSession = DB::table($this->table)
->where('session_id', $currentSessionId)
->first();
foreach ($oldSessions as $old) {
// 🔥 LOG KAYDET (termination_reason: lifo)
$this->logSessionTermination(
user: $user,
reason: 'lifo',
newSession: $newSession,
oldSession: $old
);
// Session'ı sil
DB::table($this->table)->where('id', $old->id)->delete();
}
}
}
SessionTerminationLogger.php (Yeni Service)
namespace Modules\Muzibu\App\Services;
class SessionTerminationLogger
{
public function logTermination(
User $user,
string $reason,
$newSession,
$oldSession
): void {
$logData = [
// User
'user_id' => $user->id,
'user_email' => $user->email,
'user_name' => $user->name,
'termination_reason' => $reason,
// New Session
'new_session_id' => $newSession->session_id,
'new_login_token' => $newSession->login_token,
'new_ip_address' => $newSession->ip_address,
'new_user_agent' => $newSession->user_agent,
'new_device_type' => $newSession->device_type,
'new_device_name' => $newSession->device_name,
'new_browser' => $newSession->browser,
'new_platform' => $newSession->platform,
// Geolocation (IP'den)
'new_country' => $this->getCountryFromIp($newSession->ip_address),
'new_city' => $this->getCityFromIp($newSession->ip_address),
// Old Session
'old_session_id' => $oldSession->session_id,
'old_login_token' => $oldSession->login_token ?? null,
'old_ip_address' => $oldSession->ip_address,
'old_user_agent' => $oldSession->user_agent,
'old_device_type' => $oldSession->device_type,
'old_device_name' => $oldSession->device_name,
'old_browser' => $oldSession->browser,
'old_platform' => $oldSession->platform,
'old_last_activity' => $oldSession->last_activity,
'terminated_at' => now(),
];
DB::table('session_termination_logs')->insert($logData);
// Notification gönder
$this->sendNotification($user, $logData);
}
protected function sendNotification(User $user, array $logData): void
{
// Email/SMS notification
// Event dispatch: SessionTerminatedEvent
}
}
Geolocation Service (IP → Konum)
IP adresinden şehir/ülke bilgisi çekmek için ücretsiz/ücretli servisler:
ÜCRETSİZ ip-api.com
- ✅ 45 request/dakika (ücretsiz)
- ✅ Şehir, ülke, koordinat
- ❌ HTTPS yok (ücretli)
ÜCRETLİ ipstack.com
- ✅ 10.000 req/ay (ücretsiz)
- ✅ HTTPS destekli
- ✅ ISP, timezone bilgisi
// Geolocation örnek
protected function getLocationFromIp(string $ip): array
{
try {
$response = Http::get("http://ip-api.com/json/{$ip}");
$data = $response->json();
return [
'country' => $data['country'] ?? null,
'city' => $data['city'] ?? null,
'latitude' => $data['lat'] ?? null,
'longitude' => $data['lon'] ?? null,
];
} catch (\Exception $e) {
return ['country' => null, 'city' => null];
}
}
Önerilerim & Ekstra Özellikler
1. Browser Fingerprinting
Kullanıcının cihazını benzersiz şekilde tanımlama (IP değişse bile tanır)
- ✅ FingerprintJS: Tarayıcı özellikleri, font listesi, canvas hash → Benzersiz ID
- ✅ Kullanım: VPN ile IP değiştirse bile aynı cihazı tanır
- ⚠️ Privacy: KVKK uyumlu kullanılmalı, kullanıcıya bilgi verilmeli
2. Gerçek Zamanlı Bildirim
Başka cihazdan giriş yapıldığında anında uyarı
- 📧 Email: "Yeni cihazdan giriş: iPhone, İstanbul, 22:15"
- 📱 SMS: "Muzibu hesabınıza yeni cihazdan erişildi" (opsiyonel)
- 🔔 Push Notification: Mobil app varsa anlık bildirim
3. Admin Dashboard
Yöneticiler için log görüntüleme ve filtreleme paneli
- 📊 Grafik: Günlük/aylık termination istatistikleri
- 🔍 Filtre: Kullanıcıya göre, tarihe göre, IP'ye göre arama
- 🚨 Alert: Şüpheli aktivite tespiti (aynı IP'den çok sayıda termination)
4. Kullanıcı Aktivite Geçmişi
Kullanıcı kendi aktivitelerini görebilmeli
- 📜 Geçmiş: "Son 30 günde 5 farklı cihazdan giriş yaptınız"
- ⚠️ Uyarı: "Tanımadığınız bir giriş mi? Şifrenizi değiştirin"
- 🔒 Aksiyon: "Tüm cihazlardan çıkış yap" butonu
5. GDPR/KVKK Uyumu
Kişisel veri koruma yasalarına uyum
- 📅 Retention: Logları max 90 gün saklayın (GDPR önerisi)
- 🗑️ Auto-Delete: 90 gün sonra otomatik silme cron job
- 📄 Export: Kullanıcı kendi verilerini CSV/PDF olarak indirebilmeli
6. Şüpheli Aktivite Tespiti
Otomatik güvenlik kontrolleri
- 🚨 Rate Limit: 1 saatte 10+ termination → Admin uyarısı
- 🌍 Geo Alert: Farklı ülkeden giriş → Email uyarısı
- ⏰ Time Alert: Gece 02:00'de giriş → Kullanıcıya bildirim
Implementation Adımları (Step by Step)
Migration: session_termination_logs Tablosu
Tenant database'e yeni tablo oluştur
php artisan make:migration create_session_termination_logs_table --path=database/migrations/tenant
Service: SessionTerminationLogger
Log kaydetme servisi oluştur
Modules/Muzibu/app/Services/SessionTerminationLogger.php
DeviceService.php Güncelleme
enforceDeviceLimit(), terminateSession() metodlarına log ekleme
$this->logger->logTermination(...)
Event: SessionTerminatedEvent
Event/Listener sistemi ile notification gönderme
event(new SessionTerminatedEvent($user, $logData))
Notification: Email/SMS Template
Kullanıcıya gönderilecek bildirim şablonu
resources/views/emails/session-terminated.blade.php
Admin Dashboard: Log Görüntüleme
Admin panel Livewire component
/admin/session-logs
Cron Job: Auto-Delete Old Logs
90 gün sonra logları otomatik sil (GDPR)
Schedule::daily()->deleteOldSessionLogs()