Session dan Autentikasi

Di dunia nyata, tidak semua halaman boleh diakses oleh semua orang. Session dan autentikasi (login) digunakan untuk membatasi akses — hanya pengguna yang sudah login yang bisa mengelola data.

Apa itu Session?

HTTP adalah stateless — artinya server tidak mengingat siapa yang membuat request sebelumnya. Session mengatasi masalah ini dengan menyimpan data pengguna di server.

1. User login → Server membuat session → Server kirim session ID ke browser (cookie)
2. User buka halaman lain → Browser kirim session ID → Server kenali user
3. User logout → Session dihapus

Memulai Session

Session harus dimulai di setiap halaman yang membutuhkannya, dan harus dipanggil sebelum output HTML apapun:

<?php
// WAJIB di baris paling atas, sebelum HTML apapun!
session_start();

// Menyimpan data ke session
$_SESSION['nama'] = 'User';
$_SESSION['email'] = 'User@mail.com';
$_SESSION['login'] = true;

// Membaca data dari session
echo $_SESSION['nama'];  // 'User'

// Menghapus satu data session
unset($_SESSION['nama']);

// Menghapus semua session (logout)
session_destroy();
Session harus dimulai sebelum output!
// ❌ ERROR — ada output HTML sebelum session_start()
<html>
<?php session_start(); ?>

// ✅ BENAR — session_start() di baris pertama
<?php session_start(); ?>
<html>

Membuat Sistem Login

1. Tabel User

Buat tabel users di MySQL:

USE belajar_web;

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    nama_lengkap VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- Tambah user admin (password: admin123)
-- Di PHP, password di-hash dengan password_hash()

2. Script Tambah User

Buat file setup-user.php (jalankan sekali saja):

<?php
require 'config/database.php';

// Hash password — JANGAN simpan password sebagai plain text!
$password_hash = password_hash('admin123', PASSWORD_DEFAULT);

$stmt = $pdo->prepare("INSERT INTO users (username, password, nama_lengkap) VALUES (?, ?, ?)");
$stmt->execute(['admin', $password_hash, 'Administrator']);

echo "User admin berhasil dibuat!";
echo "<br>Password hash: $password_hash";
password_hash() dan password_verify()

JANGAN PERNAH simpan password sebagai plain text di database!

// Saat REGISTRASI — hash password sebelum simpan
$hash = password_hash('admin123', PASSWORD_DEFAULT);
// Hasilnya seperti: $2y$10$abcdef...

// Saat LOGIN — verifikasi password
$cocok = password_verify('admin123', $hash);  // true
$cocok = password_verify('salah', $hash);     // false

3. Halaman Login: login.php

<?php
session_start();

// Kalau sudah login, redirect ke dashboard
if (isset($_SESSION['user_id'])) {
    header("Location: /buku-tamu/");
    exit;
}

require 'config/database.php';

$username = '';
$error = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username'] ?? '');
    $password = $_POST['password'] ?? '';

    // Cari user di database
    $stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
    $stmt->execute([$username]);
    $user = $stmt->fetch();

    // Verifikasi password
    if ($user && password_verify($password, $user['password'])) {
        // Login berhasil! Simpan data ke session
        $_SESSION['user_id'] = $user['id'];
        $_SESSION['username'] = $user['username'];
        $_SESSION['nama_lengkap'] = $user['nama_lengkap'];

        header("Location: /buku-tamu/");
        exit;
    } else {
        $error = "Username atau password salah!";
    }
}
?>

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Segoe UI', sans-serif;
            background: linear-gradient(135deg, #667eea, #764ba2);
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
        }
        .login-card {
            background: white;
            padding: 40px;
            border-radius: 16px;
            box-shadow: 0 8px 30px rgba(0,0,0,0.2);
            width: 100%;
            max-width: 380px;
        }
        h1 {
            text-align: center;
            color: #667eea;
            margin-bottom: 25px;
            font-size: 28px;
        }
        .form-group { margin-bottom: 18px; }
        label { display: block; margin-bottom: 5px; font-weight: 600; color: #444; }
        input {
            width: 100%;
            padding: 12px 14px;
            border: 2px solid #ddd;
            border-radius: 8px;
            font-size: 15px;
        }
        input:focus { outline: none; border-color: #667eea; }
        button {
            width: 100%;
            padding: 14px;
            background: #667eea;
            color: white;
            border: none;
            border-radius: 8px;
            font-size: 16px;
            cursor: pointer;
            font-weight: 600;
        }
        button:hover { background: #5a6fd6; }
        .error {
            background: #fde8e8;
            color: #dc3545;
            padding: 10px;
            border-radius: 8px;
            margin-bottom: 15px;
            text-align: center;
            font-size: 14px;
        }
    </style>
</head>
<body>
    <div class="login-card">
        <h1>🔐 Login</h1>

        <?php if ($error): ?>
            <div class="error">⚠️ <?= $error ?></div>
        <?php endif; ?>

        <form method="POST">
            <div class="form-group">
                <label for="username">Username</label>
                <input type="text" id="username" name="username"
                       value="<?= htmlspecialchars($username) ?>" required autofocus>
            </div>

            <div class="form-group">
                <label for="password">Password</label>
                <input type="password" id="password" name="password" required>
            </div>

            <button type="submit">Masuk</button>
        </form>
    </div>
</body>
</html>

4. Middleware: Cek Login

Buat file config/auth.php:

<?php
// config/auth.php — Cek apakah user sudah login

session_start();

function cekLogin() {
    if (!isset($_SESSION['user_id'])) {
        header("Location: /login.php");
        exit;
    }
}

function isLogin() {
    return isset($_SESSION['user_id']);
}

function namaUser() {
    return $_SESSION['nama_lengkap'] ?? 'Guest';
}

Cara pakai di halaman yang perlu login:

<?php
require __DIR__ . '/../config/database.php';
require __DIR__ . '/../config/auth.php';

cekLogin();  // Redirect ke login kalau belum login

// ... kode halaman yang dilindungi ...

5. Logout: logout.php

<?php
session_start();

// Hapus semua data session
$_SESSION = [];

// Hapus cookie session
if (ini_get("session.use_cookies")) {
    $params = session_get_cookie_params();
    setcookie(session_name(), '', time() - 42000,
        $params["path"], $params["domain"],
        $params["secure"], $params["httponly"]
    );
}

// Hancurkan session
session_destroy();

// Redirect ke login
header("Location: /login.php");
exit;

6. Update Header dengan Info User

Update templates/header.php:

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?= $title ?? 'Buku Tamu' ?></title>
    <link rel="stylesheet" href="/style.css">
</head>
<body>
    <nav>
        <div class="logo">📖 Buku Tamu</div>
        <ul class="menu">
            <li><a href="/buku-tamu/">Daftar Tamu</a></li>
            <li><a href="/buku-tamu/tambah.php">Tambah Baru</a></li>
            <?php if (isLogin()): ?>
                <li style="margin-left: 20px;">
                    👤 <?= htmlspecialchars(namaUser()) ?>
                    | <a href="/logout.php">Logout</a>
                </li>
            <?php endif; ?>
        </ul>
    </nav>
    <div class="container">

Selain session, PHP juga bisa menyimpan data di browser menggunakan cookie:

<?php
// Membuat cookie (tersimpan di browser)
setcookie('tema', 'dark', time() + (86400 * 30));  // Berlaku 30 hari
// 86400 = 1 hari dalam detik

// Membaca cookie
$tema = $_COOKIE['tema'] ?? 'light';
echo "Tema saat ini: $tema";

// Menghapus cookie (set expired di masa lalu)
setcookie('tema', '', time() - 3600);
SessionCookie
Disimpan diServerBrowser
UkuranTidak terbatasMaks ~4KB
KeamananLebih amanKurang aman (bisa dilihat user)
ExpiredSaat browser ditutup (default)Bisa diatur
Dipakai untukData login, keranjang belanjaPreferensi user, "ingat saya"

Contoh: Fitur "Ingat Saya"

<?php
// Di login.php — setelah login berhasil
if (isset($_POST['ingat_saya'])) {
    // Simpan token di cookie (berlaku 30 hari)
    $token = bin2hex(random_bytes(32));  // Token acak yang aman
    setcookie('remember_token', $token, time() + (86400 * 30));

    // Simpan token di database juga
    $stmt = $pdo->prepare("UPDATE users SET remember_token = ? WHERE id = ?");
    $stmt->execute([$token, $user['id']]);
}
<!-- Di form login -->
<label>
    <input type="checkbox" name="ingat_saya"> Ingat saya
</label>

Rangkuman Keamanan

:::warning Checklist Keamanan

  1. password_hash() — Hash password sebelum simpan
  2. password_verify() — Verifikasi password saat login
  3. Prepared statement — Cegah SQL Injection
  4. htmlspecialchars() — Cegah XSS
  5. session_start() — Sebelum output apapun
  6. Validasi server-side — Jangan percaya data dari browser
  7. HTTPS — Enkripsi data yang dikirim (di production) :::

Selanjutnya

Sekarang kamu sudah punya semua komponen untuk membuat aplikasi web lengkap! Mari gabungkan semuanya di Project Akhir → — Aplikasi Buku Tamu CRUD dengan login.