Menerapkan middleware pada user role system Laravel

Pada aplikasi yang lebih komplek dibutuhkan sebuah middleware untuk penyaringan HTTP request yang masuk ke aplikasi , apabila user berhasil melakukan otentikasi lalu difilter oleh middleware yang akan mengizinkan untuk melakukan request selanjutnya sesuai dengan hak akses user yg login jika tidak sesuai maka bisa di berikan pesan forbiden
enter image description here

Buat file middleware php artisan make:middleware AdminMiddleware

<?php

namespace AppHttpMiddleware;

use Closure;
use IlluminateHttpRequest;

use Auth;
use AppModelsUser;

class AdminMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  Closure(IlluminateHttpRequest): (IlluminateHttpResponse|IlluminateHttpRedirectResponse)  $next
     * @return IlluminateHttpResponse|IlluminateHttpRedirectResponse
     */
    public function handle(Request $request, Closure $next)
    {
        if( !( Auth::check() && Auth::user()->roles()->first()->name  == 'admin' ) ) abort(403);
        return $next($request);
    }
}

Buka file /app/HTTP/kernel.php

modifikasi pada blok protected $routeMiddleware

 'admin' => AppHttpMiddlewareAdminMiddleware::class,

Untuk penerapan middleware nya kita edit /routes/web.php

<?php

use IlluminateSupportFacadesRoute;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Route::group(['middleware' => ['web'] ], function () {
  Route::get('/dashboard', function () {

    return Auth::user()->roles()->first()->name =='admin' 
    ? redirect()->route('admin.dashboard')
    : redirect()->route('user.dashboard');

  })->middleware(['auth', 'verified'])->name('dashboard');

});

// ADMIN GROUP
Route::group([

  'prefix' => 'admin',
  'as' => 'admin.',
  'namespace' => 'AppHttpControllersAdmin',
  'middleware' => ['auth', 'admin']
  ], function () {
    Route::get('/', 'DashboardController@index')->name('dashboard');
});

// USER GROUP
Route::group([
    'prefix' => 'user',
    'as' => 'user.',
    'namespace' => 'AppHttpControllersUser',
    'middleware' => ['auth']
], function () {

  Route::get('/', 'DashboardController@index')->name('dashboard');

});


Route::post('/logout', [AuthenticatedSessionController::class, 'destroy'] )->middleware('auth')->name('logout');

require __DIR__.'/auth.php';

Dari routes/web.php diatas kita musti membuat 2 controller masing2 untuk role admin dan user

php artisan make:controller admin/DashboardController

kita membuat DashboardController didalam folder admin supaya website kita terorganisir dengan rapi.

enter image description here

Hal yang sama juga untuk user dashboardcontroller

php artisan make:controller user/DashboardController --resource
enter image description here

Untuk file /resources/views/ juga dipisahkan enter image description here

Untuk struktur layouts nya juga dipisahkan antara admin dan user enter image description here

Video

Membuat user roles di Laravel 9 + Breeze

Kita akan membuat sistem hak akses untuk pengguna sehingga situs web kita punya tipe pengguna sebagai admin atau user biasa enter image description here Sebelumnya ini kita sudah menginstal authentication system menggunakan Laravel 9 Breeze , dan sudah memodifikasinya supaya masuk memakai username atau email

Membuat hak akses

Buat Model hak akses pengguna dan tambahkan option -m untuk sekaligus membuat file migrasi nya

 php artisan make:model Role -m

didalam folder database/migrations , buka File: ./database/migrations/_create_roles_table.php* dan perbaharui CreateRolesTable class dengan kode dibawah ini:

class CreateRolesTable extends Migration
{
    public function up()
    {
        Schema::create('roles', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('roles');
    }
}

Kita buat hubungan many-to-many antara model User dan Role

Didalam User model // File: ./app/Models/User.php tambahkan baris kode :

public function roles() 
{
    return $this->belongsToMany(Role::class);
}

pada Role model // File: ./app/Role.php tambahkan baris kode:

public function users() 
{
    return $this->belongsToMany(User::class);
}

Bikin pivot table untuk mengasosiasikan tipe pengguna dengan hak aksesnya dengan membuat table role_user table pakai perintah dibawah ini:

 php artisan make:migration create_role_user_table

Buka File: ./database/migrations/*_create_role_user_table.php update CreateRoleUserTable class pakai kode ini:

class CreateRoleUserTable extends Migration
{

    public function up()
    {
        Schema::create('role_user', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('role_id')->unsigned();
            $table->integer('user_id')->unsigned();
        });
    }

    public function down()
    {
        Schema::dropIfExists('role_user');
    }
}

Jalankan seeders

php artisan make:seeder RoleTableSeeder php artisan make:seeder UserTableSeeder

In the database/seeds folder, open the RoleTableSeeder.php file and replace the contents with the following code:

// File: ./database/seeds/RoleTableSeeder.php
<?php 

use AppModelsRole;
use IlluminateDatabaseSeeder;

class RoleTableSeeder extends Seeder
{
    public function run()
    {
        $role_regular_user = new Role;
        $role_regular_user->name = 'user';
        $role_regular_user->save();

        $role_admin_user = new Role;
        $role_admin_user->name = 'admin';
        $role_admin_user->save();
    }
}

Buka File: ./database/seeds/UserTableSeeder.php ganti kodenya:

use IlluminateDatabaseSeeder;
use IlluminateSupportFacadesHash;
use App/Models/User;
use AppModels/Role;

class UserTableSeeder extends Seeder
{

    public function run()
    {

        $admin = new User;
        $admin->name = 'Web Administrator';
        $admin->email = '[email protected]';
        $admin->password = Hash::make('letmein');
        $admin->save();
        $admin->roles()->attach(Role::where('name', 'admin')->first());

        $user = new User;
        $user->name = 'Web User';
        $user->email =  '[email protected]';
        $user->password = Hash::make('password');
        $user->save();
        $user->roles()->attach(Role::where('name', 'user')->first());

    }
}

Buka File: ./database/seeds/DatabaseSeeder.php update public function run:

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->call([
            RoleTableSeeder::class, 
            UserTableSeeder::class,
        ]);
    }
}

Buka File: ./app/Models/User.php. tambahkan method checkRoles yg berfungsi untuk memeriksa hak akses yg dimiliki pengguna.

public function checkRoles($roles) 
{
    if ( ! is_array($roles)) {
        $roles = [$roles];    
    }

    if ( ! $this->hasAnyRole($roles)) {
        auth()->logout();
        abort(404);
    }
}

public function hasAnyRole($roles): bool
{
    return (bool) $this->roles()->whereIn('name', $roles)->first();
}

public function hasRole($role): bool
{
    return (bool) $this->roles()->where('name', $role)->first();
}

Buka File: app/HttpControllers/Auth/RegisteredUserController.php untuk membuat hak akses default sebagai pengguna biasa saat akun baru didaftarkan untuk pertama kali

protected function create(array $data)
{       
    $user = User::create([
        'name'     => $data['name'],
        'email'    => $data['email'],
        'password' => bcrypt($data['password']),
    ]);

    $user->roles()->attach(AppRole::where('name', 'user')->first());

    return $user;
}

Jalankan migrasi dan seeding

 php artisan migrate:fresh --seed

Untuk testing buka file routesWeb.php Edit

Route::get('/dashboard', function () {

    return Auth::user()->roles()->first()->name =='admin' ? view('dashboard') : abort(403);

})->middleware(['auth', 'verified'])->name('dashboard');

Saat pengguna masuk sebagai admin maka dia bisa masuk ke dashboard, selain itu maka akan forbiden

Video Tutorial

Autentikasi Username atau Email pakai Laravel 9 Breeze

Memakai Laravel 9 dan Breeze sebagai autentikasi dan untuk masuk / login memakai Username atau pun Email Untuk instalasi breeze ada di sini

enter image description here

Buka file /app/Http/Requests/Auth/LoginRequest.php ganti kode pada LoginRequest class :

 use IlluminateValidationValidationException;

class LoginRequest extends FormRequest
 {

 protected $login_by;
 protected $login;

 protected function prepareForValidation(){
  $this->login_by = filter_var($this->input('login_by'),FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
  $this->login = $this->input('login_by');
  $this->merge([ $this->login_by  => $this->login ]);
 }
  /**
   * Determine if the user is authorized to make this request.
   *
   * @return bool
  */
 public function authorize()
     {
         return true;
     }

     /**
 * Get the validation rules that apply to the request.
 *
 * @return array
 */
     public function rules()
{
    return [
        'email' => ['required_without:username','string','email','exists:users,email'],
        'username' => ['required_without:email','string','exists:users,username'],
        'password' => ['required', 'string'],
    ];
}

/**
 * Attempt to authenticate the request's credentials.
 *
 * @return void
 *
 * @throws IlluminateValidationValidationException
 */
public function authenticate()
{
    $this->ensureIsNotRateLimited();

    if (! Auth::attempt($this->only($this->login_by, 'password'), $this->boolean('remember'))) {
        RateLimiter::hit($this->throttleKey());

        throw ValidationException::withMessages([
            'login_by' => trans('auth.failed'),
        ]);
    }

    RateLimiter::clear($this->throttleKey());
}

/**
 * Ensure the login request is not rate limited.
 *
 * @return void
 *
 * @throws IlluminateValidationValidationException
 */
public function ensureIsNotRateLimited()
{
    if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
        return;
    }

    event(new Lockout($this));

    $seconds = RateLimiter::availableIn($this->throttleKey());

    throw ValidationException::withMessages([
        'email' => trans('auth.throttle', [
            'seconds' => $seconds,
            'minutes' => ceil($seconds / 60),
        ]),
    ]);
}

/**
 * Get the rate limiting throttle key for the request.
 *
 * @return string
 */
public function throttleKey()
     {
         return Str::transliterate(Str::lower($this->input('email')).'|'.$this->ip());
     }
 }

Buka File: resources/views/auth/login.blade.php update field email ganti dengan kode ini:

 <div class="mt-4">
 <x-input-label for="login_by" :value="__('Username / Email')" />
 <x-text-input id="login_by" class="block mt-1 w-full" type="text" name="login_by" :value="old('login_by')" required autofocus />
 <x-input-error :messages="$errors->get('login_by')" class="mt-2" />
 </div>

Laravel 9 Authentication Breeze Login with Username

Laravel 9 Authentication Breeze Kita akan menggunakan otentikasi laravel 9 menggunakan breeze kalau belum install laravel nya , install dulu pakai perintah composer dibawah ini (skip jika sudah ada) :

composer create-project laravel/laravel laravel

Didalam folder yg telah terinstal laravel kita bisa Install breeze Pakai composer untuk menginstal paket laravel breeze dengan perintah di bawah ini:

composer require laravel/breeze --dev

pasang scafolding nya

php artisan breeze:install

npm install && npm run dev

edit file .env untuk menyesuaikan databasenya contoh saya menjalankannya di windows Webserver (xampp) Apache MySQL

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel9
DB_USERNAME=root
DB_PASSWORD=

Lalu jalankan perintah migrasi database

php artisan migrate

Setelah selesai beberapa file yang baru telah dibuat untuk scaffolding autentikasi. sampai disini kita telah bisa menggunakan login memakai email Jalankan

php artisan serve

Lakukan pendaftaran enter image description here Setelah terdaftar misal enter image description here

Untuk modifkasi supaya bisa login menggunakan username daripada email buat migrasi tambahan

php artisan make:migration add_username_users

lalu buka File /database/migrations/*_add_username_users.php kita akan menambahkan field username setelah field name di dalam tabel Users , edit dengan kode ini:

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('username')->after('name');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('username');
        });
    }
};

Jalankan perintah

php artisan migrate --path=/database/migrations/*_add_username_users.php

tanda asteriks( * ) biasanya adalah tanggal

Kemudian buka file : /app/Http/Controllers/Auth/RegisteredUserController.php , ganti method store dengan kode ini:

public function store(Request $request)
    {
        $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'username' => ['required', 'string', 'max:255', 'unique:users'], 
            'password' => ['required', 'confirmed', RulesPassword::defaults()],
        ]);

        $user = User::create([
            'name' => $request->name,
            'username' => $request->username, 
            'password' => Hash::make($request->password),
        ]);
        event(new Registered($user));

        Auth::login($user);

        $user->roles()->attach(AppModelsRole::where('name', 'user')->first());


        return redirect(RouteServiceProvider::HOME);
    }

Buka file : /resources/views/auth/register.blade.php tambahkan kode dibawah ini:

<!-- UserName -->
<div  class="mt-4">
<x-input-label for="username" :value="__('User Name')" />
<x-text-input id="username" class="block mt-1 w-full" type="text" name="username" :value="old('username')" required autofocus /><x-input-error :messages="$errors->get('username')" class="mt-2" />
</div>

Buka file: /app/models/User.php tambahkan fillable username:

protected $fillable = [
    'name',
    'username',
    'email',
    'password',
];

Buka file: /app/Http/Requests/Auth/LoginRequest.php pada method function rules

public function rules()
    {
        return [ 
            'username' => ['required','string','exists:users,username'],
            'password' => ['required', 'string'],
        ];
    }

pada method function authenticate ganti dengan kode berikut ini:

 public function authenticate()
    {
        $this->ensureIsNotRateLimited();

        if (! Auth::attempt($this->only($this->login_by, 'password'), $this->boolean('remember'))) {
            RateLimiter::hit($this->throttleKey());

            throw ValidationException::withMessages([
                'login_by' => trans('auth.failed'),
            ]);
        }

        RateLimiter::clear($this->throttleKey());
    }

public function ensureIsNotRateLimited()
    {
        if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
            return;
        }

        event(new Lockout($this));

        $seconds = RateLimiter::availableIn($this->throttleKey());

        throw ValidationException::withMessages([
                'username' => trans('auth.throttle', [
                'seconds' => $seconds,
                'minutes' => ceil($seconds / 60),
            ]),
        ]);
    }



  public function throttleKey()
    {
        return Str::transliterate(Str::lower($this->input('username')).'|'.$this->ip());
    }

Buka file : /resources/views/auth/login.blade.php tambahkan kode dibawah ini:

<!-- Username -->
            <div class="mt-4">
                <x-input-label for="username" :value="__('Username  ')" />

                <x-text-input id="login_by" class="block mt-1 w-full" type="text" name="username" :value="old('username')" required autofocus />
                <x-input-error :messages="$errors->get('username')" class="mt-2" />
            </div>

Selanjutnya kita bisa login menggunakan username untuk menggantikan email

Perbaikan Laravel Public Folder Redirect di Cpanel atau pun localhost

enter image description here

Biasanya kalau install Laravel di Cpanel shared hosting Saat setelah install Laravel di Cpanel maka kita akan disuguhkan

<?php
header("refresh: 5; https://demo.baliwebmaker.com/public/");
echo '<title>Laravel Installed</title><div style="background: #e9ffed; border: 1px solid #b0dab7; padding: 15px;" align="center" >
     <font size="5" color="#182e7a">Laravel is installed successfully.</font><br /><br />
     <font size="4">Laravel is a Framework and doesn't have an index page.<br /><br />
     You will be redirected to its "public" folder in 5 seconds...<br /><br />
Laravel is a clean and classy framework for PHP web development.Freeing you from spaghetti code, Laravel helps you create wonderful applications using simple, expressive syntax. Development should be a creative experience that you enjoy, not something that is painful. Enjoy the fresh air.
   </font></div>';
?>

Ganti dengan

<?php

use IlluminateContractsHttpKernel;
use IlluminateHttpRequest;define('LARAVEL_START', microtime(true));
require __DIR__.'/vendor/autoload.php';

$app = require_once __DIR__.'/bootstrap/app.php';

$kernel = $app->make(Kernel::class);

$response = tap($kernel->handle(
        $request = Request::capture()
    ))->send();$kernel->terminate($request, $response);  

lalu simpan file index.php

Untuk .htaccess nya di root

<IfModule mod_rewrite.c>
Options +FollowSymLinks
RewriteEngine On

RewriteCond %{REQUEST_URI} !^/public/ 

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f


RewriteRule ^(.*)$ /public/$1 
#RewriteRule ^ index.php [L]
RewriteRule ^(/)?$ public/index.php [L] 
</IfModule>

lalu di htaccess di folder public

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews -Indexes
    </IfModule>

    RewriteEngine On

    # Handle Authorization Header
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} (.+)/$
    RewriteRule ^ %1 [L,R=301]

    # Send Requests To Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>

Setting ini berjalan juga di localhost yg menjalankan Apache webserver. saat kita tidak memakai php artisan serve untuk melihat website yg sedang kita buat misal kita akses pakai local domain http://projectlaravel.lokal