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>