如何在Laravel中验证网络地址与Netmask(CIDR)的匹配性?
Laravel IP地址与子网掩码匹配验证方案
1. 从CIDR字符串提取IP和子网掩码
如果用户输入的是CIDR格式(如192.168.1.100/25),直接分割字符串即可分离IP地址和CIDR前缀;若输入的是点分格式子网掩码(如255.255.255.128),可以转成对应的CIDR前缀:
// 分割CIDR字符串 $cidr = '192.168.1.100/25'; list($ip, $cidrPrefix) = explode('/', $cidr, 2); // 点分掩码转CIDR前缀 function maskToCidr(string $mask): int { $binaryMask = implode('', array_map(function($octet) { return str_pad(decbin((int)$octet), 8, '0', STR_PAD_LEFT); }, explode('.', $mask))); return strlen(rtrim($binaryMask, '0')); } // 示例:将255.255.255.128转为25 $cidrPrefix = maskToCidr('255.255.255.128');
2. 计算有效网络范围
通过IP地址和CIDR前缀,计算网段的起始(网络地址)和结束(广播地址),两者之间的IP即为有效范围:
function getNetworkRange(string $ip, int $cidrPrefix): array { $ipLong = ip2long($ip); $maskLong = ~((1 << (32 - $cidrPrefix)) - 1) & 0xFFFFFFFF; $networkLong = $ipLong & $maskLong; $broadcastLong = $networkLong | (~$maskLong & 0xFFFFFFFF); return [ 'network' => long2ip($networkLong), 'broadcast' => long2ip($broadcastLong), 'network_long' => $networkLong, 'broadcast_long' => $broadcastLong ]; } // 示例:计算192.168.1.100/25的范围 $range = getNetworkRange('192.168.1.100', 25); // 返回:network=192.168.1.0, broadcast=192.168.1.127
3. Laravel中实现验证逻辑
方法一:自定义可复用验证规则
- 生成规则文件:
php artisan make:rule ValidIpForNetmask
- 编辑
app/Rules/ValidIpForNetmask.php:
<?php namespace App\Rules; use Illuminate\Contracts\Validation\Rule; class ValidIpForNetmask implements Rule { protected int $cidrPrefix; public function __construct(string $netmask) { // 自动转换子网掩码格式为CIDR前缀 if (strpos($netmask, '.') !== false) { $this->cidrPrefix = $this->maskToCidr($netmask); } else { $this->cidrPrefix = (int)ltrim($netmask, '/'); } } public function passes($attribute, $value): bool { $ipLong = ip2long($value); if (!$ipLong) return false; $maskLong = ~((1 << (32 - $this->cidrPrefix)) - 1) & 0xFFFFFFFF; $networkLong = $ipLong & $maskLong; // 验证IP与掩码运算后是否等于网段起始地址 return $ipLong === ($networkLong & $maskLong); } public function message(): string { return 'Invalid network address for that netmask'; } private function maskToCidr(string $mask): int { $binaryMask = implode('', array_map(function($octet) { return str_pad(decbin((int)$octet), 8, '0', STR_PAD_LEFT); }, explode('.', $mask))); return strlen(rtrim($binaryMask, '0')); } }
- 在控制器中调用:
public function store(Request $request) { $validated = $request->validate([ 'ip_address' => ['required', 'ip', new ValidIpForNetmask($request->netmask)], 'netmask' => ['required', 'regex:/^(\/[0-9]{1,2}|((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))$/'], ]); // 验证通过后的业务逻辑 }
方法二:在FormRequest中直接处理
如果不需要复用规则,可直接在请求类中添加验证逻辑:
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class NetworkRequest extends FormRequest { public function authorize(): bool { return true; } public function rules(): array { return [ 'ip_address' => 'required|ip', 'netmask' => ['required', 'regex:/^(\/[0-9]{1,2}|((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))$/'], ]; } public function withValidator($validator) { $validator->after(function ($validator) { $ip = $this->ip_address; $netmask = $this->netmask; // 转换子网掩码为CIDR前缀 $cidr = strpos($netmask, '.') !== false ? strlen(rtrim(implode('', array_map(fn($oct) => str_pad(decbin((int)$oct), 8, '0', STR_PAD_LEFT), explode('.', $netmask))), '0')) : (int)ltrim($netmask, '/'); $ipLong = ip2long($ip); $maskLong = ~((1 << (32 - $cidr)) - 1) & 0xFFFFFFFF; $networkLong = $ipLong & $maskLong; if ($ipLong !== ($networkLong & $maskLong)) { $validator->errors()->add('ip_address', 'Invalid network address for that netmask'); } }); } }
控制器中使用该请求类:
public function store(NetworkRequest $request) { // 验证通过,执行业务逻辑 }
内容的提问来源于stack exchange,提问作者Abid Raheem
相关产品推荐
相关产品推荐

