Mengatur Middleware PHP Router Miladrahimi
Dalam kesempatan kali ini saya akan menjelaskan tentang bagaimana mengatur router pada salah satu library php router yang dibuat oleh milad rahimi seorang programmer berkebangsaan Iran. Saya tidak akan terlalu menjelaskan Panjang lebar mengenai metode pengrutean aplikasi menggunakan php router miladrahimi, sebab kamu bisa membacanya sendiri pada laman github miladrahimi/phprouter.
Librari PHP Router dari miladrahimi sendiri tidaklah menyediakan handle ‘middleware’ secara langsung, sehingga kamu perlu membuatnya sendiri terlebih dahulu. Jadi disini kita akan membuat dua buah middleware yang akan kita tambahkan dalam router group dari library php routernya. middleware pertama untuk mengecek user terautentifikasi atau tidak sedangkan middleware yang kedua adalah untuk mengecek hak akses dari user yang terautentifikasi
AuthMiddleware.php
<?php
namespace Auth;
use Psr\Http\Message\ServerRequestInterface;
use Laminas\Diactoros\Response\JsonResponse;
class AuthMiddleware
{
/**
* Middleware handler
*
* @param ServerRequestInterface $request
* @param callable $next
* @return mixed
*/
public function handle(ServerRequestInterface $request, callable $next)
{
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
if (!$this->isAuthenticated($request)) {
//$this->logUnauthorizedAccess($request);
return new JsonResponse(
['error' => 'Unauthorized', 'message' => 'Anda harus login untuk mengakses halaman ini.'],
401
);
}
return $next($request);
}
private function isAuthenticated(ServerRequestInterface $request): bool
{
// Misalnya cek apakah user ada di sesi
if (isset($_SESSION['user'])) {
return true;
}
// Tambahkan cek lain jika perlu, seperti token atau cookie
// return $this->validateToken($request);
return false;
}
private function logUnauthorizedAccess(ServerRequestInterface $request): void
{
$logMessage = sprintf(
"[%s] Unauthorized access attempt from IP: %s\n",
date('Y-m-d H:i:s'),
$request->getServerParams()['REMOTE_ADDR'] ?? 'unknown'
);
file_put_contents(__DIR__ . '/../logs/auth.log', $logMessage, FILE_APPEND);
}
}
RoleMiddleware.php
<?php
namespace Auth;
use Psr\Http\Message\ServerRequestInterface;
use Laminas\Diactoros\Response\JsonResponse;
class RoleMiddleware
{
/**
* Peran yang diizinkan
*
* @var array
*/
private array $allowedRoles;
/**
* Constructor untuk RoleMiddleware
*
* @param array|string $roles
*/
public function __construct(array|string $roles)
{
$this->allowedRoles = (array)$roles; // Ubah string menjadi array jika diperlukan
}
/**
* Middleware handler
*
* @param ServerRequestInterface $request
* @param callable $next
* @return mixed
*/
private function logForbiddenAccess(ServerRequestInterface $request): void
{
$logMessage = sprintf(
"[%s] Forbidden access attempt from IP: %s. User role: %s\n",
date('Y-m-d H:i:s'),
$request->getServerParams()['REMOTE_ADDR'] ?? 'unknown',
$_SESSION['user']['role']
);
file_put_contents(__DIR__ . '/../logs/role.log', $logMessage, FILE_APPEND);
}
public function handle(ServerRequestInterface $request, callable $next)
{
if (!$this->isAuthenticated()) {
return new JsonResponse(
['error' => 'Unauthorized', 'message' => 'Anda harus login untuk mengakses halaman ini.'],
401
);
}
if (!$this->hasRequiredRole()) {
//$this->logForbiddenAccess($request); // Log jika akses ditolak berdasarkan peran
return new JsonResponse(
['error' => 'Forbidden', 'message' => 'Anda tidak memiliki akses ke halaman ini.'],
403
);
}
return $next($request);
}
/**
* Memeriksa apakah pengguna terautentikasi
*
* @return bool
*/
private function isAuthenticated(): bool
{
return isset($_SESSION['user']);
}
/**
* Memeriksa apakah pengguna memiliki peran yang sesuai
*
* @return bool
*/
private function hasRequiredRole(): bool
{
return in_array($_SESSION['user']['role'], $this->allowedRoles, true);
}
}
Setelah middleware dibuat kita hanya perlu mengaksesnya dalam router group dengan bentuk array. Dibawah ini merupakan beberapa variasi atau contoh penggunaan middleware dalam routernya :
<?php
use MiladRahimi\PhpRouter\Router;
use Auth\AuthMiddleware;
use Auth\RoleMiddleware;
$router = Router::create();
// Grup rute untuk admin dengan middleware autentikasi dan otorisasi
$router->group(['middleware' => [new AuthMiddleware(), new RoleMiddleware('administrator')]], function (Router $router) {
$router->get('/admin/dashboard', [AdminController::class, 'dashboard']);
$router->get('/admin/settings', [AdminController::class, 'settings']);
});
//only AuthMiddleware
$router->group(['middleware' => new AuthMiddleware()], function (Router $router) {
$router->get('/user/profile', [UserController::class, 'profile']);
$router->post('/user/settings', [UserController::class, 'updateSettings']);
});
//another
$router->group(['middleware' => new AuthMiddleware()], function (Router $router) {
// Hanya pengguna dengan peran tertentu yang dapat mengakses ini
$router->group(['middleware' => new RoleMiddleware(['administrator', 'editor'])], function (Router $router) {
$router->get('/posts/manage', [PostController::class, 'managePosts']);
$router->post('/posts/create', [PostController::class, 'createPost']);
});
});
//another
$router->group(['middleware' => new AuthMiddleware()], function (Router $router) {
// Rute untuk admin
$router->group(['middleware' => new RoleMiddleware('administrator')], function (Router $router) {
$router->get('/admin/dashboard', [AdminController::class, 'dashboard']);
$router->get('/admin/settings', [AdminController::class, 'settings']);
});
// Rute untuk editor
$router->group(['middleware' => new RoleMiddleware('editor')], function (Router $router) {
$router->get('/editor/posts', [EditorController::class, 'viewPosts']);
$router->post('/editor/posts/update', [EditorController::class, 'updatePost']);
});
// Rute umum untuk semua pengguna yang login
$router->get('/user/profile', [UserController::class, 'profile']);
});
Memisah - Misah Router
Selanjutnya dimisalkan router yang kita bangun itu tidak disatukan kedalam satu file melainkan dipisah - pisah menjadi beberapa dengan tujuan untuk membuat kode terlihat lebih rafi serta untuk mengelompokan kode tersebut berdasarkan fungsi atau hak aksesnya, misal jika kode tersebut dibangun untuk admin maka kita buat router file khusus admin atau router untuk user biasa maka ada router khusus untuk user biasa.
Dimisalkan kita tahu routernya secara terpisah didalam folder lain yang berbeda. Dimisalkan bahwa kamu punya struktur direktori seperti berikut :
/config
routes.php <-- File utama untuk memanggil semua rute
/routes
admin.php <-- File untuk rute admin
user.php <-- File untuk rute pengguna
public.php <-- File untuk rute publik (tanpa autentikasi)
/routes/admin.php
use MiladRahimi\PhpRouter\Router;
use Auth\AuthMiddleware;
use Auth\RoleMiddleware;
return function (Router $router) {
$router->group(['middleware' => [new AuthMiddleware(), new RoleMiddleware('administrator')]], function (Router $router) {
$router->get('/admin/dashboard', [AdminController::class, 'dashboard']);
$router->post('/admin/settings', [AdminController::class, 'updateSettings']);
});
};
/routes/user.php
use MiladRahimi\PhpRouter\Router;
use Auth\AuthMiddleware;
return function (Router $router) {
$router->group(['middleware' => new AuthMiddleware()], function (Router $router) {
$router->get('/user/profile', [UserController::class, 'profile']);
$router->post('/user/settings', [UserController::class, 'updateSettings']);
});
};
/routes/public.php
use MiladRahimi\PhpRouter\Router;
return function (Router $router) {
$router->get('/home', [PublicController::class, 'home']);
$router->get('/about', [PublicController::class, 'about']);
$router->post('/contact', [PublicController::class, 'contact']);
};
Selanjutnya panggil semua file tersebut melalui satu file router utama /config/routes.php
use MiladRahimi\PhpRouter\Router;
// Inisialisasi router
$router = Router::create();
// Tambahkan view jika diperlukan
$router->setupView(__DIR__ . '/../views');
// Load semua file rute
foreach (glob(__DIR__ . '/../routes/*.php') as $routeFile) {
$routeDefinition = require $routeFile;
if (is_callable($routeDefinition)) {
$routeDefinition($router);
}
}
$router->dispatch();
Middleware Global
Kita juga bisa menggunakan metode lain dalam looping pada saat memanggil file - file router yang terpisahnya. Contoh misalnya kita ingin mengatur middleware secara global untuk semua rute, maka kamu bisa memodifikasi file /config/routes.php menjadi seperti berikut :
$router->group(['middleware' => new AuthMiddleware()], function (Router $router) {
// Load semua file rute
foreach (glob(__DIR__ . '/../routes/*.php') as $routeFile) {
$routeDefinition = require $routeFile;
if (is_callable($routeDefinition)) {
$routeDefinition($router);
}
}
});
File diatas bisa saja kita tambah fungsi lain untuk mengecek atau validasi pemanggilan file :
foreach (glob(__DIR__ . '/../routes/*.php') as $routeFile) {
$routeDefinition = require $routeFile;
if (!is_callable($routeDefinition)) {
error_log("Invalid route definition in file: {$routeFile}");
continue;
}
$routeDefinition($router);
}
Dalam konteks kode global middleware diatas, tentu setiap halaman yang berada pada rute akan dicek autentifikasinya, apakah user memiliki hak akses atau tidak
Catatan :
Middleware perlu diakses dalam bentuk array didalam router group
['middleware' => [new AuthMiddleware()]]Jika kita butuh middleware tambahan maka cukup tambahkan nilainya kedalam array
['middleware' => [new AuthMiddleware(), new RoleMiddleware(['admin'])]]