Materi Lengkap: Implementasi Login Manual dengan Laravel (Session + Middleware Auth)

1. Tujuan Pembelajaran

Setelah menyelesaikan materi ini, peserta diharapkan mampu:

  1. Memahami konsep autentikasi berbasis session di Laravel.
  2. Membuat sistem login dan logout dengan controller khusus.
  3. Merancang routing group yang dilindungi middleware.
  4. Menerapkan middleware auth untuk membatasi akses ke halaman tertentu.
  5. Menghubungkan sistem login dengan fitur yang sudah ada (contoh: manajemen produk).

2. Gambaran Umum Alur Autentikasi

Sebelum terjun ke kode, mari kita pahami alur yang akan kita bangun. Ini adalah "cerita" dari sistem login kita:

  1. Pengguna yang belum login mencoba mengakses halaman produk (/products).
  2. Middleware auth akan menangkap permintaan itu, melihat bahwa tidak ada session login, lalu mengalihkan (redirect) pengguna ke halaman login (/login).
  3. Di halaman login, pengguna mengisi email dan password, lalu menekan tombol submit.
  4. Data kredensial (email & password) dikirim ke server untuk divalidasi dan dicocokkan dengan data di tabel users.
  5. Jika cocok (authentication successful), Laravel akan membuat session login untuk pengguna tersebut. Pengguna kemudian diarahkan ke halaman yang awalnya ingin dia tuju (/products).
  6. Kini, ketika pengguna mengakses /products lagi, middleware auth akan memeriksa session yang valid dan mengizinkan akses.
  7. Saat pengguna memilih logout, session tersebut akan dihancurkan, dan pengguna dikembalikan ke halaman login.

Dengan memahami cerita ini, kode yang kita tulis nanti akan lebih bermakna.


3. Persiapan: Database dan Tabel Users

Laravel sudah menyediakan migration untuk tabel users secara default. Pastikan Anda telah menjalankan migrasi untuk membuat tabelnya di database.

php artisan migrate

Tabel users ini memiliki kolom-kolom penting seperti id, name, email, password (yang disimpan dalam bentuk terenkripsi/hash), dan remember_token. Kita akan menggunakan email dan password untuk proses login.


4. Membangun Sistem Login: Controller dan View

Langkah pertama adalah membuat Controller yang menangani logika login dan logout. Kita akan beri nama AuthController.

php artisan make:controller AuthController

File: app/Http/Controllers/AuthController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class AuthController extends Controller
{
    /**
     * Menampilkan halaman form login.
     */
    public function showLoginForm()
    {
        // Mengembalikan view 'auth.login'
        return view('auth.login');
    }

    /**
     * Memproses data login yang dikirim dari form.
     */
    public function login(Request $request)
    {
        // 1. Validasi input form
        $credentials = $request->validate([
            'email'    => 'required|email',
            'password' => 'required|min:6',
        ]);

        // 2. Attempt Login: Mencocokkan kredensial dengan database
        if (Auth::attempt($credentials)) {
            // 3. Regenerasi Session (untuk keamanan)
            $request->session()->regenerate();
            // 4. Redirect ke URL yang dimaksud sebelumnya, atau ke halaman default ('/products')
            return redirect()->intended('/products');
        }

        // 5. Jika attempt gagal, kembali ke halaman login dengan pesan error
        return back()->withErrors([
            'email' => 'Email atau password yang dimasukkan salah.',
        ]);
    }

    /**
     * Memproses permintaan logout.
     */
    public function logout(Request $request)
    {
        // 1. Logout user (menghapus session auth)
        Auth::logout();
        // 2. Invalidasi session saat ini
        $request->session()->invalidate();
        // 3. Regenerasi CSRF token (untuk keamanan)
        $request->session()->regenerateToken();
        // 4. Redirect ke halaman login
        return redirect('/login');
    }
}

Penjelasan Kode Controller:

  • Fungsi attempt() dari Facade Auth adalah inti dari login. Ia akan mengenkripsi password inputan, mencocokkannya dengan hash di database, dan secara otomatis menginisiasi session jika berhasil.
  • redirect()->intended() adalah fitur cerdas Laravel. Ia akan mengingat URL yang ingin diakses pengguna sebelum di-intercept middleware, sehingga setelah login, pengguna langsung dibawa ke tujuan awalnya.
  • Proses logout harus menghancurkan session sepenuhnya (invalidate()) dan membuat token baru (regenerateToken()) untuk mencegah serangan session fixation.

Selanjutnya, kita buat view untuk halaman login. Kita akan menggunakan Bootstrap untuk styling yang cepat dan rapi.

File: resources/views/auth/login.blade.php

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Halaman Login</title>
    <!-- Menghubungkan CSS Bootstrap 5 -->
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light d-flex align-items-center" style="min-height: 100vh;">
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-5">
                <div class="card shadow">
                    <div class="card-header bg-primary text-white text-center py-3">
                        <h4>Login ke Sistem</h4>
                    </div>
                    <div class="card-body p-4">

                        <!-- Menampilkan pesan error jika validasi/login gagal -->
                        @if($errors->any())
                        <div class="alert alert-danger alert-dismissible fade show" role="alert">
                            {{ $errors->first() }}
                            <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
                        </div>
                        @endif

                        <form action="{{ route('login') }}" method="POST">
                            @csrf <!-- Token CSRF Wajib untuk keamanan form Laravel -->

                            <div class="mb-3">
                                <label for="email" class="form-label">Alamat Email</label>
                                <input type="email"
                                       class="form-control @error('email') is-invalid @enderror"
                                       id="email"
                                       name="email"
                                       value="{{ old('email') }}"
                                       placeholder="[email protected]"
                                       required>
                            </div>

                            <div class="mb-4">
                                <label for="password" class="form-label">Password</label>
                                <input type="password"
                                       class="form-control @error('password') is-invalid @enderror"
                                       id="password"
                                       name="password"
                                       placeholder="Masukkan password"
                                       required>
                            </div>

                            <div class="d-grid">
                                <button type="submit" class="btn btn-primary btn-lg">Masuk</button>
                            </div>
                        </form>

                    </div>
                    <div class="card-footer text-center text-muted py-3">
                        <small>Demo Sistem Login Laravel</small>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <!-- Menghubungkan JavaScript Bootstrap -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

5. Menghubungkan Semuanya: Routing

Routing adalah penghubung antara URL, Controller, dan Middleware. Kita akan atur rute untuk publik (login) dan privat (yang dilindungi).

File: routes/web.php

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthController;
use App\Http\Controllers\ProductController; // Asumsi kita punya controller untuk produk

// --- ROUTE PUBLIK YANG SUDAH ADA SEBELUMNYA (TANPA MIDDLEWARE) ---
Route::get('/', [HomeController::class, 'home']);
Route::get('/about', [HomeController::class, 'about']);
Route::get('/contact', [HomeController::class, 'contact']);
Route::get('/tentang-aplikasi', [HomeController::class, 'tentang_aplikasi']);

// --- ROUTE PUBLIK (TANPA MIDDLEWARE) ---
// Hanya pengguna belum login yang bisa akses
Route::get('/login', [AuthController::class, 'showLoginForm'])->name('login');
Route::post('/login', [AuthController::class, 'login']);
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');

// --- ROUTE PRIVAT (DILINDUNGI MIDDLEWARE 'auth') ---
// Hanya pengguna yang sudah login yang bisa akses
Route::middleware(['auth'])->group(function () {
    // Semua route di dalam grup ini otomatis terlindungi

    // Contoh: Route untuk manajemen produk
    Route::get('/products', [ProductController::class, 'index'])->name('products.index');
    Route::get('/products/create', [ProductController::class, 'create'])->name('products.create');
    Route::post('/products', [ProductController::class, 'store'])->name('products.store');
    Route::get('/products/{product}/edit', [ProductController::class, 'edit'])->name('products.edit');
    Route::put('/products/{product}', [ProductController::class, 'update'])->name('products.update');
    Route::delete('/products/{product}', [ProductController::class, 'destroy'])->name('products.destroy');

    // Contoh route lain yang perlu login
    Route::get('/dashboard', function () {
        return view('dashboard');
    })->name('dashboard');
});

Mengapa route login dan logout berada di luar grup middleware auth? Karena jika mereka dilindungi, pengguna yang belum login tidak akan bisa mengakses halaman login sama sekali—dan akan terjebak dalam loop redirect.


6. Middleware Auth: Sang Penjaga Gerbang

Source: buildwithangga.com

Ini adalah bagian kunci dari materi kita. Middleware auth adalah penjaga yang berdiri di depan route privat kita. Cara kerjanya sederhana namun kuat:

  1. Intercept: Setiap kali ada request masuk ke route di dalam grupnya, middleware auth dijalankan terlebih dahulu.
  2. Cek Session: Middleware ini memeriksa, "Apakah request ini memiliki session login yang valid?"
  3. Keputusan:
    • Jika YA: Request diteruskan ke controller tujuan (misal, ProductController).
    • Jika TIDAK: Request dibelokkan (redirect) ke route bernama 'login'. Laravel secara otomatis menambahkan parameter ?next= yang berisi URL yang gagal diakses.

Kita tidak perlu menulis kode middleware ini karena sudah disediakan oleh Laravel. Kita hanya perlu menerapkannya dengan benar pada route, seperti yang telah dilakukan di atas.


7. Menyiapkan Data Pengguna (Seeder)

Untuk keperluan pengembangan dan testing, kita perlu membuat data user contoh. Kita buat sebuah Seeder.

php artisan make:seeder UserSeeder

File: database/seeders/UserSeeder.php

<?php

namespace Database\Seeders;

use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;

class UserSeeder extends Seeder
{
    public function run()
    {
        // Hapus data lama jika ada (optional)
        User::truncate();

        // Buat user admin contoh
        User::create([
            'name' => 'Administrator',
            'email' => '[email protected]',
            'password' => Hash::make('password123'), // Password di-hash
        ]);

        // Bisa ditambahkan user lain di sini
        User::create([
            'name' => 'User Biasa',
            'email' => '[email protected]',
            'password' => Hash::make('rahasia456'),
        ]);

        $this->command->info('Sample user created successfully!');
    }
}

Jalankan seeder ini untuk menambahkan user ke database:

php artisan db:seed --class=UserSeeder

Sekarang Anda bisa login dengan email [email protected] dan password password123.


Oke, kita integrasikan materi Menu Dinamis Berdasarkan Status Login ke dalam materi yang sama. Saya akan tambahkan bagian ini setelah middleware auth, dengan penjelasan yang ringkas namun komprehensif.


Lanjutan: Integrasi Menu Navbar Dinamis

8. Implementasi Menu Dinamis Berdasarkan Status Login

Setelah sistem login dan middleware berjalan, langkah logis berikutnya adalah menyesuaikan tampilan navbar berdasarkan apakah pengguna sudah login atau belum. Ini penting untuk UX yang baik dan keamanan.

Kita akan modifikasi layout utama (biasanya resources/views/layouts/main.blade.php)

Harap di perhatikan, untuk nama file layouts bisa saja berbeda beda tergantung pada project masing masing!.

untuk memiliki dua kondisi:

  1. Menu untuk Pengguna Belum Login: Menampilkan link Login.
  2. Menu untuk Pengguna Sudah Login: Menampilkan link Dashboard/Produk, nama user, dan tombol Logout.

File: resources/views/layouts/main.blade.php (Revisi)

<!DOCTYPE html>
<html lang="id">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@yield('title', 'CRUD Produk')</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>

<body>
    <!-- Navbar -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a href="/" class="navbar-brand">CRUD Produk</a>

            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
                <span class="navbar-toggler-icon"></span>
            </button>

            <div class="collapse navbar-collapse" id="navbarNav">
                <!-- Menu Kiri (Selalu Tampil) -->
                <ul class="navbar-nav me-auto">
                    <li class="nav-item">
                        <a href="/" class="nav-link {{ request()->is('/') ? 'active' : '' }}">Home</a>
                    </li>
                    <li class="nav-item">
                        <a href="/about" class="nav-link {{ request()->is('about') ? 'active' : '' }}">About</a>
                    </li>
                </ul>

                <!-- Menu Kanan (Kondisional) -->
                <ul class="navbar-nav">

                    <!-- Menu yang hanya tampil jika user SUDAH login -->
                    @auth
                    <!-- Menu untuk user yang SUDAH login -->
                        <li class="nav-item">
                            <a href="{{ url('/dashboard') }}"
                                class="nav-link {{ request()->is('dashboard') ? 'active' : '' }}">
                                Dashboard
                            </a>
                        </li>
                        <li class="nav-item">
                            <a href="{{ route('products.index') }}"
                                class="nav-link {{ request()->is('products*') ? 'active' : '' }}">
                                Produk
                            </a>
                        </li>
                        
                        <li class="nav-item dropdown">
                            <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">
                                {{ Auth::user()->name }} <!-- Menampilkan nama user -->
                            </a>
                            <ul class="dropdown-menu dropdown-menu-end">
                                <li><a class="dropdown-item" href="{{ url('/dashboard') }}">Dashboard</a></li>
                                <li>
                                    <hr class="dropdown-divider">
                                </li>
                                <!-- Form Logout -->
                                <li>
                                    <form method="POST" action="{{ route('logout') }}">
                                        @csrf
                                        <button type="submit" class="dropdown-item text-danger">Logout</button>
                                    </form>
                                </li>
                            </ul>
                        </li>
                    @else
                        <!-- Menu untuk user yang BELUM login -->
                        <li class="nav-item">
                            <a href="{{ route('login') }}" class="nav-link">Login</a>
                        </li>
                    @endauth
                </ul>
            </div>
        </div>
    </nav>

    <!-- Konten Utama -->
    <div class="container mt-4">
        @yield('content')
    </div>

    <!-- Script Bootstrap -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>

    <!-- Script Tambahan per Halaman -->
    @stack('scripts')
</body>

</html>

Penjelasan Kode Navbar Dinamis:

  1. Blade Directives @auth dan @endauth: Direktif ini adalah cara paling sederhana untuk mengecek status login.Kode di dalamnya hanya akan ditampilkan jika user sudah terautentikasi.

  2. Blade Directives @guest dan @endguest: Alternatif untuk @auth, khusus untuk pengguna yang belum login. Dalam kode di atas, saya menggunakan @else dari @auth untuk efisiensi.

  3. Menampilkan Data User: {{ Auth::user()->name }} menampilkan nama user yang sedang login. Auth::user() mengembalikan object model User, jadi kita bisa akses semua kolom seperti email, id, dll.

  4. Form Logout di Dropdown: Logout harus menggunakan form POST karena tindakan ini merubah state aplikasi. Form kecil ini mengirimkan request ke route logout dengan token CSRF.

  5. Highlight Menu Aktif: {{ request()->is('products*') ? 'active' : '' }} menambahkan class active jika URL saat ini berada di path products atau turunannya. Memberikan visual feedback yang baik untuk user.

Adaptasi View Lainnya:

Setiap view yang menggunakan layout ini (misal products/index.blade.php), cukup diawali dengan:

@extends('layouts.app')

@section('title', 'Daftar Produk')

@section('content')
    <!-- Konten halaman produk di sini -->
@endsection

Rute Dashboard:

Pastikan Anda menambahkan rute untuk dashboard di dalam grup middleware auth:

// Di dalam Route::middleware(['auth'])->group(function () { ... });
Route::get('/dashboard', function () {
    return view('dashboard'); // Buat file resources/views/dashboard.blade.php
})->name('dashboard');

Keuntungan Implementasi Ini:

  1. User Experience Lebih Baik: Pengguna langsung tahu status login mereka.
  2. Keamanan: Menu terproteksi tidak ditampilkan ke pengguna anonim.
  3. Konsistensi: Semua halaman menggunakan navbar yang sama dengan logika kondisional.
  4. Mudah Dikembangkan: Cukup bungkus menu dengan @auth / @guest untuk kontrol tampilan.

Testing Navbar Dinamis:

  1. Akses homepage tanpa login: Hanya tampil menu Home, About, dan Login.
  2. Login dengan akun admin: Menu Produk muncul, nama user tampil di dropdown dengan opsi Logout.
  3. Klik Logout: Kembali ke state pertama.

9. Kesimpulan

Dengan integrasi navbar dinamis ini, sistem login kita sudah komplit dari backend hingga frontend. Pengguna mendapatkan pengalaman yang mulus dan aman.

Rangkuman yang telah kita bangun:

  1. ✅ Sistem Login Manual dengan Controller dan Session
  2. ✅ Middleware Auth sebagai Penjaga Route
  3. ✅ Navbar Dinamis Berdasarkan Status Login
  4. ✅ Form Logout yang Aman

Ringkasan Struktur Folder Proyek Laravel 12 (Setelah Implementasi Login + Navbar)
📁 laravel-12-project/
│
├── 📁 app/
│   ├── 📁 Http/
│   │   └── 📁 Controllers/
│   │       ├── AuthController.php         # Controller login/logout (BARU)
│   │       ├── ProductController.php      # Controller produk (sudah ada)
│   │       └── Controller.php
│   │
│   ├── 📁 Models/
│   │   ├── User.php                       # Model User (default)
│   │   └── Product.php                    # Model Produk (jika ada)
│   │
│   └── ...
│
├── 📁 bootstrap/
│   └── app.php                           # Tempat definisi middleware
│
├── 📁 database/
│   ├── 📁 migrations/
│   │   ├── 2014_10_12_000000_create_users_table.php
│   │   ├── ..._create_products_table.php  # Migration produk (jika ada)
│   │   └── ...
│   │
│   ├── 📁 seeders/
│   │   ├── DatabaseSeeder.php
│   │   └── UserSeeder.php                 # Seeder user (BARU)
│   │
│   └── ...
│
├── 📁 resources/
│   ├── 📁 views/
│   │   ├── 📁 layouts/
│   │   │   └── app.blade.php              # Layout utama (SUDAH DIUPDATE)
│   │   │
│   │   ├── 📁 auth/                       # Folder baru untuk auth views
│   │   │   └── login.blade.php            # View login (BARU)
│   │   │
│   │   ├── 📁 products/                   # View produk (sudah ada)
│   │   │   ├── index.blade.php
│   │   │   ├── create.blade.php
│   │   │   └── ...
│   │   │
│   │   └── dashboard.blade.php            # View dashboard (BARU, opsional)
│   │
│   └── ...
│
├── 📁 routes/
│   └── web.php                            # SUDAH DIUPDATE dengan routing auth
│
├── .env
├── composer.json
└── ...