Membuat Login Multi-Role dengan PHP untuk Pemula

Login multi-role dengan PHP berarti satu sistem login bisa membedakan hak akses user, misalnya admin, editor, dan user. Jawaban cepatnya: simpan role di tabel users, hash password dengan password_hash(), verifikasi login dengan password_verify(), lalu lindungi setiap halaman memakai helper seperti requireRole('admin').

Halaman ini cocok kalau kamu sudah memahami Session dan Autentikasi, Keamanan Password, dan Authentication vs Authorization. Kita akan membuat versi sederhana yang mudah dibaca pemula, tetapi tetap memakai prinsip penting: password tidak disimpan polos, akses tidak hanya disembunyikan dari menu, dan semua halaman sensitif harus dicek di server.

Gambaran Fitur

Project kecil ini punya tiga role:

RoleBoleh Akses
admindashboard admin, kelola user, lihat semua data
editordashboard editor, kelola konten
userdashboard user biasa

Alur aplikasinya:

  1. User membuka login.php.
  2. PHP mencari username di database.
  3. Password dicek dengan password_verify().
  4. Jika benar, session menyimpan user_id, username, dan role.
  5. Halaman tertentu memanggil requireRole() untuk membatasi akses.

Struktur Folder

Gunakan struktur sederhana berikut:

login-multi-role/
├── config/
│   ├── database.php
│   └── auth.php
├── admin/
│   └── index.php
├── editor/
│   └── index.php
├── user/
│   └── index.php
├── login.php
├── logout.php
└── seed-user.php

Untuk latihan, seed-user.php dipakai sekali untuk membuat akun awal. Di project production, pembuatan user sebaiknya lewat halaman admin atau proses registrasi yang terkontrol.

Membuat Tabel Users

Buat database dan tabel:

CREATE DATABASE belajar_login_role;
USE belajar_login_role;

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    nama VARCHAR(100) NOT NULL,
    role ENUM('admin', 'editor', 'user') NOT NULL DEFAULT 'user',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Kolom role menentukan hak akses. Kita memakai ENUM agar nilai role terbatas pada daftar yang jelas.

Koneksi Database

Isi config/database.php:

<?php
declare(strict_types=1);

$host = '127.0.0.1';
$db = 'belajar_login_role';
$user = 'root';
$pass = '';

$pdo = new PDO(
    "mysql:host={$host};dbname={$db};charset=utf8mb4",
    $user,
    $pass,
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    ]
);

Kalau koneksi database gagal, baca juga Koneksi Database dan Cara Mengatasi SQLSTATE Error di PHP PDO.

Membuat User Awal

Isi seed-user.php:

<?php
declare(strict_types=1);

require __DIR__ . '/config/database.php';

$users = [
    ['admin', 'admin123', 'Administrator', 'admin'],
    ['editor', 'editor123', 'Editor Konten', 'editor'],
    ['budi', 'user123', 'Budi Santoso', 'user'],
];

$stmt = $pdo->prepare("
    INSERT INTO users (username, password, nama, role)
    VALUES (?, ?, ?, ?)
");

foreach ($users as [$username, $password, $nama, $role]) {
    $hash = password_hash($password, PASSWORD_DEFAULT);
    $stmt->execute([$username, $hash, $nama, $role]);
}

echo 'User awal berhasil dibuat.';

Jalankan sekali dari browser atau terminal, lalu hapus file ini setelah selesai agar akun contoh tidak dibuat berulang-ulang.

Membuat Helper Auth

Isi config/auth.php:

<?php
declare(strict_types=1);

if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

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

function currentUser(): array
{
    return [
        'id' => $_SESSION['user_id'] ?? null,
        'username' => $_SESSION['username'] ?? null,
        'nama' => $_SESSION['nama'] ?? null,
        'role' => $_SESSION['role'] ?? null,
    ];
}

function requireLogin(): void
{
    if (!isLogin()) {
        header('Location: /login.php');
        exit;
    }
}

function requireRole(string ...$roles): void
{
    requireLogin();

    $userRole = $_SESSION['role'] ?? '';

    if (!in_array($userRole, $roles, true)) {
        http_response_code(403);
        echo '403 - Akses ditolak.';
        exit;
    }
}

requireRole() menerima lebih dari satu role. Misalnya halaman konten boleh diakses admin dan editor.

Membuat Halaman Login

Isi login.php:

<?php
declare(strict_types=1);

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

if (isLogin()) {
    header('Location: /user/index.php');
    exit;
}

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

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

    $stmt = $pdo->prepare('SELECT * FROM users WHERE username = ? LIMIT 1');
    $stmt->execute([$username]);
    $user = $stmt->fetch();

    if ($user && password_verify($password, $user['password'])) {
        session_regenerate_id(true);

        $_SESSION['user_id'] = $user['id'];
        $_SESSION['username'] = $user['username'];
        $_SESSION['nama'] = $user['nama'];
        $_SESSION['role'] = $user['role'];

        if ($user['role'] === 'admin') {
            header('Location: /admin/index.php');
        } elseif ($user['role'] === 'editor') {
            header('Location: /editor/index.php');
        } else {
            header('Location: /user/index.php');
        }
        exit;
    }

    $error = 'Username atau password salah.';
}
?>

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <title>Login Multi-Role PHP</title>
</head>
<body>
    <h1>Login Multi-Role</h1>

    <?php if ($error !== ''): ?>
        <p style="color: red;"><?= htmlspecialchars($error) ?></p>
    <?php endif; ?>

    <form method="POST">
        <label>
            Username
            <input type="text" name="username" value="<?= htmlspecialchars($username) ?>" required>
        </label>

        <label>
            Password
            <input type="password" name="password" required>
        </label>

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

Perhatikan dua detail penting:

  1. session_regenerate_id(true) dijalankan setelah login berhasil.
  2. Pesan error dibuat umum: jangan beri tahu apakah username atau password yang salah.

Melindungi Halaman Berdasarkan Role

Contoh halaman admin, admin/index.php:

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

requireRole('admin');

$user = currentUser();
?>

<h1>Dashboard Admin</h1>
<p>Halo, <?= htmlspecialchars($user['nama']) ?>.</p>
<p>Halaman ini hanya untuk role admin.</p>
<a href="/logout.php">Logout</a>

Contoh halaman editor, editor/index.php:

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

requireRole('admin', 'editor');
?>

<h1>Dashboard Editor</h1>
<p>Admin dan editor boleh membuka halaman ini.</p>

Contoh halaman user biasa, user/index.php:

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

requireLogin();

$user = currentUser();
?>

<h1>Dashboard User</h1>
<p>Role kamu: <?= htmlspecialchars($user['role']) ?></p>

Logout

Isi logout.php:

<?php
session_start();

$_SESSION = [];

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

session_destroy();

header('Location: /login.php');
exit;

Kesalahan yang Sering Terjadi

Hanya Menyembunyikan Menu Admin

Menu yang disembunyikan bukan keamanan. User tetap bisa mengetik URL manual. Setiap file admin harus tetap memanggil requireRole('admin').

Password Disimpan Plain Text

Jangan simpan password asli di database. Gunakan password_hash() saat menyimpan dan password_verify() saat login.

Lupa session_start()

Session harus dimulai sebelum membaca atau menulis $_SESSION. Helper auth.php di atas sudah menangani ini dengan session_status().

Role Tidak Dicek Setelah Login

Login hanya menjawab "siapa user ini". Role menjawab "boleh melakukan apa". Keduanya harus dipisahkan.

Redirect Setelah Login Tidak Diikuti exit

Setelah header('Location: ...'), selalu panggil exit agar kode di bawahnya tidak ikut berjalan.

FAQ

Apakah role harus memakai ENUM?

Tidak wajib. Bisa juga memakai tabel roles terpisah. Untuk project pemula, ENUM cukup sederhana. Untuk aplikasi besar, tabel role lebih fleksibel.

Apakah admin boleh mengubah role dirinya sendiri?

Sebaiknya hati-hati. Di aplikasi nyata, perubahan role admin perlu validasi tambahan agar tidak mengunci semua akun admin.

Apakah halaman user biasa perlu requireRole('user')?

Kalau semua user login boleh masuk, cukup requireLogin(). Pakai requireRole() ketika halaman hanya boleh untuk role tertentu.

Bagaimana kalau satu user punya banyak role?

Gunakan tabel relasi seperti user_roles. Panduan ini memakai satu role per user agar konsep dasarnya mudah dipahami dulu.

Bacaan Terkait