Sesi login: lifetime 5 menit + sesi kadaluarsa dianggap mati
- SESSION_LIFETIME=5 di .env (sebelumnya 120 menit idle) - User::sessionTokenIsAlive() kini cek umur sesi (file mtime / DB last_activity) vs masa hidup, bukan sekadar ada/tidaknya file — user yang sesinya habis langsung bisa login lagi tanpa nunggu GC atau Paksa Logout - 2 test baru (sesi kadaluarsa → boleh login; sesi fresh → tetap terkunci)
This commit is contained in:
+24
-2
@@ -152,7 +152,10 @@ class User extends Authenticatable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cek apakah token sesi aktif masih "hidup" (session file/DB masih ada).
|
* Cek apakah token sesi aktif masih "hidup".
|
||||||
|
* Sesi dianggap hidup hanya jika file/row masih ada DAN belum lewat masa
|
||||||
|
* hidup (session.lifetime). Sesi yang sudah kadaluarsa dianggap mati,
|
||||||
|
* sehingga user bebas login lagi di perangkat mana pun.
|
||||||
* Driver array/lain → dianggap hidup (aman, tolak login ganda).
|
* Driver array/lain → dianggap hidup (aman, tolak login ganda).
|
||||||
*/
|
*/
|
||||||
public function sessionTokenIsAlive(): bool
|
public function sessionTokenIsAlive(): bool
|
||||||
@@ -162,12 +165,31 @@ class User extends Authenticatable
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$lifetimeSeconds = (int) config('session.lifetime') * 60;
|
||||||
|
|
||||||
return match (config('session.driver')) {
|
return match (config('session.driver')) {
|
||||||
'file' => file_exists(storage_path('framework/sessions/'.$token)),
|
'file' => $this->fileSessionIsAlive($token, $lifetimeSeconds),
|
||||||
'database' => \Illuminate\Support\Facades\DB::table(config('session.table', 'sessions'))
|
'database' => \Illuminate\Support\Facades\DB::table(config('session.table', 'sessions'))
|
||||||
->where('id', $token)
|
->where('id', $token)
|
||||||
|
->where('last_activity', '>=', time() - $lifetimeSeconds)
|
||||||
->exists(),
|
->exists(),
|
||||||
default => true,
|
default => true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cek sesi berbasis file: file ada DAN aktivitas terakhir (mtime) masih
|
||||||
|
* dalam masa hidup. Laravel menulis ulang file sesi di setiap request,
|
||||||
|
* jadi mtime = aktivitas terakhir user.
|
||||||
|
*/
|
||||||
|
protected function fileSessionIsAlive(string $token, int $lifetimeSeconds): bool
|
||||||
|
{
|
||||||
|
$path = storage_path('framework/sessions/'.$token);
|
||||||
|
|
||||||
|
if (! is_file($path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (time() - (int) filemtime($path)) <= $lifetimeSeconds;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,4 +94,48 @@ class SingleSessionTest extends TestCase
|
|||||||
|
|
||||||
$this->assertNull($user->refresh()->active_session_token);
|
$this->assertNull($user->refresh()->active_session_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_sesi_yang_sudah_kadaluarsa_dianggap_mati(): void
|
||||||
|
{
|
||||||
|
// Produksi pakai driver file — uji perilaku sesungguhnya
|
||||||
|
config()->set('session.driver', 'file');
|
||||||
|
|
||||||
|
$user = $this->makeUser();
|
||||||
|
$sessionId = str_repeat('a', 40); // id sesi valid (format 40 char)
|
||||||
|
$user->forceFill(['active_session_token' => $sessionId])->save();
|
||||||
|
|
||||||
|
// Simulasikan file sesi lama: sudah lewat masa hidup (10 menit lalu)
|
||||||
|
$file = storage_path('framework/sessions/'.$sessionId);
|
||||||
|
file_put_contents($file, 'expired');
|
||||||
|
touch($file, time() - 600);
|
||||||
|
|
||||||
|
$this->assertFalse($user->refresh()->sessionTokenIsAlive());
|
||||||
|
|
||||||
|
// Login kedua harusnya DITERIMA sekarang
|
||||||
|
$this->post('/login', [
|
||||||
|
'username' => 'bksession',
|
||||||
|
'password' => 'rahasia123',
|
||||||
|
])->assertRedirect();
|
||||||
|
|
||||||
|
$this->assertAuthenticated();
|
||||||
|
@unlink($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_sesi_yang_masih_hidup_dianggap_hidup(): void
|
||||||
|
{
|
||||||
|
// Produksi pakai driver file — uji perilaku sesungguhnya
|
||||||
|
config()->set('session.driver', 'file');
|
||||||
|
|
||||||
|
$user = $this->makeUser();
|
||||||
|
$sessionId = str_repeat('b', 40);
|
||||||
|
$user->forceFill(['active_session_token' => $sessionId])->save();
|
||||||
|
|
||||||
|
// File sesi baru (aktivitas sekarang)
|
||||||
|
$file = storage_path('framework/sessions/'.$sessionId);
|
||||||
|
file_put_contents($file, 'fresh');
|
||||||
|
touch($file);
|
||||||
|
|
||||||
|
$this->assertTrue($user->refresh()->sessionTokenIsAlive());
|
||||||
|
@unlink($file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user