mwi – Selamat siang sobat pembelajar MWI, selanjutnya kitaa akan belajar autentikasi. Autentikasi yang kita pelajari menggunakan authentication buatan sendiri terlebih dahulu, bukan langsung CodeIgniter Shield. Alasannya, proyek ini merupakan bahan pembelajaran untuk pemula. Dengan cara ini, siswa dapat memahami proses login, verifikasi password, session, filter, dan pembatasan hak akses secara bertahap.
Tutorial ini akan menghasilkan fitur:
- Login menggunakan email dan password.
- Logout dengan mengakhiri session.
- Dua jenis pengguna:
admindanpetugas. - Proteksi halaman agar pengguna yang belum login tidak bisa masuk.
- Pembatasan halaman berdasarkan role.
- Menampilkan nama pengguna yang sedang login.
- Mengarahkan pengguna ke dashboard setelah login.
Asumsi struktur tabel users: kita menggunakan kolom id, name, email, password, role, is_active, created_at, dan updated_at. Jika tabel users sudah dibuat melalui migration sebelumnya, jangan membuat tabel kedua. Cocokkan kolom yang ada dengan struktur ini terlebih dahulu.
Daftar Isi
1. Gambaran alur authentication

2. Struktur Folder Tambahan
Kita akan menambahkan beberapa file berikut:
app/
├── Config/
│ └── Routes.php
├── Controllers/
│ └── Auth.php
├── Database/
│ └── Seeds/
│ └── AdminUserSeeder.php
├── Filters/
│ ├── AuthFilter.php
│ └── RoleFilter.php
├── Models/
│ └── UserModel.php
└── Views/
└── auth/
└── login.php
Kita juga akan mengubah file app/Config/Filters.php untuk mendaftarkan filter authentication dan role.
3. Membuat UserModel
Model berfungsi sebagai penghubung antara aplikasi dan tabel users.
Buat file:
app/Models/UserModel.php
<?php
namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $table = 'users';
protected $primaryKey = 'id';
protected $returnType = 'array';
protected $allowedFields = [
'name',
'email',
'password',
'role',
'is_active',
];
protected $useTimestamps = true;
protected $dateFormat = 'datetime';
}
Penjelasan:
$tablemenentukan tabel yang digunakan, yaituusers.$primaryKeymenentukan primary key tabel.$returnTypemembuat hasil query berbentuk array.$allowedFieldsmenentukan kolom yang boleh kita isi melalui Model.$useTimestampsmengaktifkan pengelolaancreated_atdanupdated_at.
Pastikan tabel users memang memiliki kolom timestamp jika $useTimestamps bernilai true.
4. Membuat akun admin dan petugas
Untuk pembelajaran, kita akan membuat akun awal melalui Seeder. Dengan demikian, kita tidak perlu membuat halaman registrasi publik.
Buat seeder:
php spark make:seeder AdminUserSeeder
Buka file:
app/Database/Seeds/AdminUserSeeder.php
Isi dengan kode berikut:
<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class AdminUserSeeder extends Seeder
{
public function run()
{
$users = [
[
'name' => 'Administrator',
'email' => 'admin@sipus.test',
'password' => password_hash(
'admin123',
PASSWORD_DEFAULT
),
'role' => 'admin',
'is_active' => 1,
],
[
'name' => 'Petugas Perpustakaan',
'email' => 'petugas@sipus.test',
'password' => password_hash(
'petugas123',
PASSWORD_DEFAULT
),
'role' => 'petugas',
'is_active' => 1,
],
];
foreach ($users as $user) {
$exists = $this->db->table('users')
->where('email', $user['email'])
->countAllResults();
if ($exists === 0) {
$this->db->table('users')->insert($user);
}
}
}
}
Jalankan seeder:
php spark db:seed AdminUserSeeder
Akun untuk pengujian:
| Role | Password | |
|---|---|---|
| Admin | admin@sipus.test | admin123 |
| Petugas | petugas@sipus.test | petugas123 |
Catatan keamanan
Password di atas hanya untuk pembelajaran lokal. Aplikasi produksi harus menggunakan password yang kuat dan mengganti password awal. Kita menggunakan password_hash() agar password tidak tersimpan dalam bentuk teks biasa.
5. Membuat Auth Controller
Controller ini menangani proses login dan logout.
Buat file:
app/Controllers/Auth.php
<?php
namespace App\Controllers;
use App\Models\UserModel;
class Auth extends BaseController
{
public function login()
{
// Jika sudah login, langsung ke dashboard.
if (session()->get('logged_in')) {
return redirect()->to('/dashboard');
}
return view('auth/login', [
'errors' => session()->getFlashdata('errors') ?? [],
]);
}
public function attemptLogin()
{
$rules = [
'email' => 'required|valid_email',
'password' => 'required',
];
if (! $this->validate($rules)) {
return redirect()->back()
->withInput()
->with('errors', $this->validator->getErrors());
}
$email = trim($this->request->getPost('email'));
$password = $this->request->getPost('password');
$model = new UserModel();
$user = $model->where('email', $email)
->first();
// Periksa apakah akun ditemukan dan aktif.
if (! $user || (int) $user['is_active'] !== 1) {
return redirect()->back()
->withInput()
->with('error', 'Email atau password salah.');
}
// Verifikasi password yang tersimpan di database.
if (! password_verify($password, $user['password'])) {
return redirect()->back()
->withInput()
->with('error', 'Email atau password salah.');
}
// Buat ulang session ID setelah login.
session()->regenerate();
// Simpan informasi pengguna ke session.
session()->set([
'user_id' => $user['id'],
'user_name' => $user['name'],
'user_email'=> $user['email'],
'user_role' => $user['role'],
'logged_in' => true,
]);
return redirect()->to('/dashboard')
->with('success', 'Selamat datang di SIPUS.');
}
public function logout()
{
// Hapus seluruh data session.
session()->destroy();
return redirect()->to('/login')
->with('success', 'Anda berhasil logout.');
}
}
Penjelasan bagian penting:
login()menampilkan halaman login.attemptLogin()memvalidasi email dan password.UserModelmencari pengguna berdasarkan email.password_verify()membandingkan password yang kita masukkan dengan hash di database.session()->regenerate()membuat ID session baru setelah login.session()->set()menyimpan identitas dan role pengguna.logout()mengakhiri session pengguna.
Kita sengaja memberikan pesan kesalahan yang sama untuk email tidak ada dan password salah. Cara ini mengurangi informasi yang dapat kita gunakan orang lain untuk menebak akun.
6. Membuat halaman Login
Buat folder:
app/Views/auth/
Kemudian buat file:
app/Views/auth/login.php
Untuk halaman login, kita gunakan tampilan mandiri agar tidak menampilkan sidebar aplikasi sebelum pengguna berhasil login.
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Login SIPUS</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
rel="stylesheet"
>
</head>
<body class="bg-light">
<div class="container">
<div class="row justify-content-center align-items-center min-vh-100">
<div class="col-md-5 col-lg-4">
<div class="card shadow-sm border-0">
<div class="card-body p-4">
<h3 class="text-center mb-2">SIPUS</h3>
<p class="text-center text-muted mb-4">
Sistem Informasi Perpustakaan Sekolah
</p>
<?php if (session()->getFlashdata('success')): ?>
<div class="alert alert-success">
<?= esc(session()->getFlashdata('success')) ?>
</div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="alert alert-danger">
<?= esc(session()->getFlashdata('error')) ?>
</div>
<?php endif; ?>
<?php if (! empty($errors)): ?>
<div class="alert alert-danger">
<ul class="mb-0">
<?php foreach ($errors as $error): ?>
<li><?= esc($error) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<form action="<?= site_url('login') ?>" method="post">
<?= csrf_field() ?>
<div class="mb-3">
<label class="form-label">Email</label>
<input
type="email"
name="email"
class="form-control"
value="<?= esc(old('email')) ?>"
required
autofocus
>
</div>
<div class="mb-3">
<label class="form-label">Password</label>
<input
type="password"
name="password"
class="form-control"
required
>
</div>
<button type="submit" class="btn btn-primary w-100">
Login
</button>
</form>
</div>
</div>
<p class="text-center text-muted mt-3 small">
© <?= date('Y') ?> SIPUS
</p>
</div>
</div>
</div>
</body>
</html>
Halaman ini menampilkan formulir email dan password. Kita juga menambahkan CSRF token melalui csrf_field() untuk membantu melindungi formulir dari serangan Cross-Site Request Forgery.
7. Membuat AuthFilter
Authentication filter bertugas memeriksa apakah pengguna sudah login sebelum membuka halaman tertentu.
Buat file:
app/Filters/AuthFilter.php
<?php
namespace App\Filters;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
class AuthFilter implements FilterInterface
{
public function before(
RequestInterface $request,
$arguments = null
) {
if (! session()->get('logged_in')) {
return redirect()->to('/login')
->with('error', 'Silakan login terlebih dahulu.');
}
}
public function after(
RequestInterface $request,
ResponseInterface $response,
$arguments = null
) {
// Tidak ada proses setelah request.
}
}
Cara kerja:
- Jika session
logged_inbernilaitrue, pengguna dapat melanjutkan request. - Jika belum login, filter mengarahkan pengguna ke halaman login.
8. Membuat RoleFilter
Selain memastikan pengguna sudah login, kita juga perlu membatasi akses berdasarkan role.
Misalnya:
- Admin dapat membuka halaman pengelolaan pengguna.
- Petugas tidak boleh membuka halaman tersebut.
- Keduanya dapat membuka halaman buku dan transaksi sesuai aturan aplikasi.
Buat file:
app/Filters/RoleFilter.php
<?php
namespace App\Filters;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\Filters\FilterInterface;
class RoleFilter implements FilterInterface
{
public function before(
RequestInterface $request,
$arguments = null
) {
$userRole = session()->get('user_role');
if (
empty($arguments) ||
! in_array($userRole, $arguments, true)
) {
return redirect()->to('/dashboard')
->with('error', 'Anda tidak memiliki hak akses.');
}
}
public function after(
RequestInterface $request,
ResponseInterface $response,
$arguments = null
) {
// Tidak ada proses setelah request.
}
}
Contohnya, ketika route menggunakan role:admin, filter akan memeriksa apakah user_role sama dengan admin.
9. Mendaftarkan Filter
Sekarang kita perlu mendaftarkan kedua filter agar dapat kita gunakan oleh Routes.
Buka:
app/Config/Filters.php
Cari bagian $aliases, kemudian tambahkan:
public array $aliases = [
// Alias bawaan CodeIgniter tetap dipertahankan.
'auth' => \App\Filters\AuthFilter::class,
'role' => \App\Filters\RoleFilter::class,
];
Penting: jangan menghapus alias bawaan seperti csrf, toolbar, dan honeypot. Tambahkan dua alias tersebut ke dalam daftar yang sudah ada. Jika file menggunakan deklarasi alias yang berbeda, cukup masukkan kedua nama tersebut ke array alias yang sama.
10. Mengatur Routes
Buka:
app/Config/Routes.php
Pertahankan route lain yang sudah ada, kemudian atur route authentication dan halaman yang terlindungi seperti berikut:
<?php
$routes->get('login', 'Auth::login');
$routes->post('login', 'Auth::attemptLogin');
$routes->get('logout', 'Auth::logout', ['filter' => 'auth']);
// Halaman yang harus login terlebih dahulu.
$routes->group('', ['filter' => 'auth'], static function ($routes) {
$routes->get('/', 'Dashboard::index');
$routes->get('dashboard', 'Dashboard::index');
$routes->get('categories', 'Categories::index');
$routes->get('books', 'Books::index');
// Tambahkan route anggota dan transaksi nanti.
});
// Contoh halaman khusus admin.
$routes->group('admin', ['filter' => 'role:admin'], static function ($routes) {
$routes->get('users', 'Users::index');
});
Ada satu hal penting: route admin/users pada contoh di atas juga harus dilindungi authentication. Karena itu, cara yang lebih tepat adalah meletakkan group admin di dalam group authentication:
$routes->group('', ['filter' => 'auth'], static function ($routes) {
$routes->get('/', 'Dashboard::index');
$routes->get('dashboard', 'Dashboard::index');
$routes->get('categories', 'Categories::index');
$routes->get('books', 'Books::index');
$routes->group('admin', ['filter' => 'role:admin'], static function ($routes) {
$routes->get('users', 'Users::index');
});
});
Gunakan contoh kedua sebagai susunan akhir untuk halaman yang membutuhkan kedua filter.
Jangan lupa: route CRUD yang sudah dibuat sebelumnya untuk Categories dan Books juga harus dimasukkan ke dalam group auth. Jangan mendaftarkan route yang sama dua kali.
11. Menampilkan nama pengguna di Navbar
Sekarang kita tampilkan nama pengguna yang sedang login.
Buka:
app/Views/layouts/navbar.php
Tambahkan bagian berikut pada area navbar:
<div class="d-flex align-items-center gap-3">
<span class="text-white">
<?= esc(session()->get('user_name')) ?>
(<?= esc(session()->get('user_role')) ?>)
</span>
<a href="<?= site_url('logout') ?>"
class="btn btn-outline-light btn-sm">
Logout
</a>
</div>
Dengan kode tersebut, navbar akan menampilkan nama dan role pengguna.
Contoh tampilan:
SIPUS
Administrator (admin)
Logout
Ilustrasi area navbar setelah login.
Pastikan bagian navbar tersebut hanya ditampilkan pada layout aplikasi yang sudah dilindungi filter auth.
12. Menguji Authentication
Sekarang jalankan server CodeIgniter:
php spark serve
Buka browser:
http://localhost:8080/login
Coba login menggunakan akun admin.
Data pengujian loginEmail Admin
admin@sipus.test
SalinPassword Admin
admin123
SalinEmail Petugas
petugas@sipus.test
SalinPassword Petugas
petugas123
Salin
Lakukan pengujian berikut:
| No. | Pengujian | Hasil yang diharapkan |
|---|---|---|
| 1 | Membuka /dashboard sebelum login | Dialihkan ke login |
| 2 | Login dengan email benar | Masuk dashboard |
| 3 | Login dengan password salah | Muncul pesan kesalahan |
| 4 | Login dengan akun tidak aktif | Login ditolak |
| 5 | Membuka /books sebelum login | Dialihkan ke login |
| 6 | Logout | Session berakhir |
| 7 | Membuka halaman admin dengan role petugas | Akses ditolak |
Catatan: pengujian nomor 7 baru dapat dilakukan setelah membuat controller Users dan route admin tersebut.
13. Perbaikan penting: gunakan POST untuk Logout
Ada satu penyempurnaan keamanan dari contoh sebelumnya. Sebaiknya proses logout menggunakan metode POST, bukan GET, karena logout mengubah status session pengguna.
Pada app/Config/Routes.php, gunakan:
$routes->post('logout', 'Auth::logout', ['filter' => 'auth']);
Kemudian, pada app/Views/layouts/navbar.php, ganti tautan logout dengan formulir berikut:
<form action="<?= site_url('logout') ?>" method="post" class="d-inline">
<?= csrf_field() ?>
<button type="submit" class="btn btn-outline-light btn-sm">
Logout
</button>
</form>
Dengan demikian, proses logout juga menyertakan CSRF token.
14. Contoh susunan Routes akhir
Agar tidak membingungkan, berikut contoh susunan route yang bisa kita jadikan acuan. Gabungkan dengan route lain yang sudah ada dan hindari mendaftarkan URL yang sama dua kali.
app/Config/Routes.php
<?php
// Authentication: dapat diakses tanpa login.
$routes->get('login', 'Auth::login');
$routes->post('login', 'Auth::attemptLogin');
// Semua halaman berikut membutuhkan login.
$routes->group('', ['filter' => 'auth'], static function ($routes) {
$routes->get('/', 'Dashboard::index');
$routes->get('dashboard', 'Dashboard::index');
// Categories
$routes->get('categories', 'Categories::index');
$routes->get('categories/new', 'Categories::new');
$routes->post('categories/create', 'Categories::create');
$routes->get('categories/edit/(:num)', 'Categories::edit/$1');
$routes->post('categories/update/(:num)', 'Categories::update/$1');
$routes->post('categories/delete/(:num)', 'Categories::delete/$1');
// Books
$routes->get('books', 'Books::index');
$routes->get('books/new', 'Books::new');
$routes->post('books/create', 'Books::create');
$routes->get('books/edit/(:num)', 'Books::edit/$1');
$routes->post('books/update/(:num)', 'Books::update/$1');
$routes->post('books/delete/(:num)', 'Books::delete/$1');
// Contoh halaman khusus admin.
$routes->group('admin', ['filter' => 'role:admin'], static function ($routes) {
$routes->get('users', 'Users::index');
});
// Logout hanya dapat dilakukan setelah login.
$routes->post('logout', 'Auth::logout');
});
Route Users::index di atas merupakan contoh untuk modul pengelolaan pengguna yang akan kita buat nanti. Jangan mengaksesnya sebelum controller Users tersedia.
Perhatikan juga bahwa seluruh route CRUD Kategori dan Buku sekarang berada di dalam group auth. Artinya, pengguna harus login sebelum mengaksesnya.
15. Memahami pembagian hak akses
Untuk SIPUS, kita dapat menggunakan pembagian hak akses seperti berikut.
| Fitur | Admin | Petugas |
|---|---|---|
| Dashboard | Ya | Ya |
| Melihat kategori | Ya | Ya |
| Mengelola kategori | Ya | Terbatas |
| Melihat buku | Ya | Ya |
| Mengelola buku | Ya | Terbatas |
| Mengelola anggota | Ya | Ya |
| Mengelola peminjaman | Ya | Ya |
| Mengelola pengguna | Ya | Tidak |
| Melihat laporan | Ya | Ya |
Tabel ini merupakan rancangan hak akses. Kita akan menerapkannya pada setiap route ketika modul terkait selesai kita buat.
Perlu kita ingat, menyembunyikan tombol pada tampilan saja tidak cukup. Kita tetap harus membatasi akses melalui filter di sisi server.
16. Troubleshooting
Berikut beberapa masalah yang mungkin muncul ketika menerapkan tutorial.
| Masalah | Penyebab yang mungkin | Solusi |
|---|---|---|
Table users doesn’t exist | Migration tabel users belum berjalan | Periksa migration dan jalankan php spark migrate |
Unknown column is_active | Kolom belum tersedia | Sesuaikan migration dan struktur tabel |
| Login selalu gagal | Password di database belum berupa hash yang sesuai | Buat ulang akun menggunakan password_hash() |
| Session tidak tersimpan | Konfigurasi session bermasalah | Periksa konfigurasi app/Config/Session.php |
| Class AuthFilter not found | Nama file, class, atau namespace salah | Periksa app/Filters/AuthFilter.php |
| Redirect berulang ke login | Session logged_in tidak tersimpan | Periksa konfigurasi session dan proses login |
| Akses ditolak untuk admin | Nilai role tidak sama dengan admin | Periksa nilai kolom users.role |
Untuk memeriksa migration, jalankan:
php spark migrate:status
Untuk melihat akun yang sudah dibuat, periksa tabel users melalui phpMyAdmin atau database client. Pastikan kolom password berisi hash, bukan password biasa.
17. Kesimpulan
Pada tahap ini, SIPUS sudah memiliki dasar authentication yang terdiri dari:
- Login menggunakan email dan password.
- Verifikasi password dengan
password_verify(). - Penyimpanan identitas pengguna melalui session.
- Logout untuk mengakhiri session.
- Authentication filter untuk melindungi halaman.
- Role filter untuk membatasi akses berdasarkan peran pengguna.
Authentication ini menjadi fondasi sebelum kita membuat modul Anggota dan Transaksi. Dengan begitu, setiap fitur baru dapat langsung menggunakan sistem login dan hak akses yang sudah tersedia.
18. Urutan pengerjaan setelah tutorial
Agar proses pembelajaran SIPUS tetap terstruktur, saya sarankan urutannya:
- Authentication Login dan Logout — tahap yang sedang kita kerjakan.
- CRUD Members (Anggota).
- CRUD Peminjaman.
- Detail Peminjaman (beberapa buku dalam satu transaksi).
- Pengembalian Buku.
- Laporan Peminjaman.
- Penyempurnaan hak akses Admin dan Petugas.
Target berikutnya adalah CRUD Members (Anggota). Setelah authentication berhasil kita uji, kita bisa langsung membuat modul tersebut dengan memanfaatkan session dan role yang sudah tersedia. Demikian tutorial kali ini semoga bermanfaat yah (ms).