53 lines
1.3 KiB
PHP
53 lines
1.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Middleware;
|
|
|
|
use Closure;
|
|
use Illuminate\Http\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
final class AdminIpAllowlist
|
|
{
|
|
public function handle(Request $request, Closure $next): Response
|
|
{
|
|
$allowed = config('admin.allowed_ips', []);
|
|
|
|
// 개발(local/testing)에서는 allowlist 비어있으면 전체 허용
|
|
if (!$allowed && !app()->environment('production')) {
|
|
return $next($request);
|
|
}
|
|
|
|
if (!$allowed) {
|
|
abort(403, 'admin ip not allowed');
|
|
}
|
|
|
|
$ip = $request->ip();
|
|
|
|
foreach ($allowed as $rule) {
|
|
if ($this->matchIp($ip, $rule)) {
|
|
return $next($request);
|
|
}
|
|
}
|
|
|
|
abort(403, 'admin ip not allowed');
|
|
}
|
|
|
|
private function matchIp(string $ip, string $rule): bool
|
|
{
|
|
if (strpos($rule, '/') === false) {
|
|
return $ip === $rule;
|
|
}
|
|
|
|
[$subnet, $mask] = explode('/', $rule, 2);
|
|
$mask = (int) $mask;
|
|
|
|
$ipLong = ip2long($ip);
|
|
$subLong = ip2long($subnet);
|
|
|
|
if ($ipLong === false || $subLong === false || $mask < 0 || $mask > 32) return false;
|
|
|
|
$maskLong = -1 << (32 - $mask);
|
|
return (($ipLong & $maskLong) === ($subLong & $maskLong));
|
|
}
|
|
}
|