如何修改Laravel中间件代码,检查请求IP是否在子网掩码范围内?
如何修改Laravel中间件代码,检查请求IP是否在子网掩码范围内?
嘿,我来帮你搞定这个问题!你现在的代码只能匹配精确的单个IP,要支持像12.23.34.0/27这种带子网掩码的白名单条目,咱们得把原来的contains()判断逻辑换掉,改成能自定义匹配规则的方式。
直接上修改后的完整代码,你可以直接替换中间件里的判断部分:
$requestIP = $request->ip(); $whitelist = collect([ '127.0.0.1', '12.23.34.0/27' ]); // 遍历白名单,只要有一个条目匹配就通过 $isAuthorized = $whitelist->some(function ($entry) use ($requestIP) { // 处理普通单个IP的情况(不带子网掩码) if (strpos($entry, '/') === false) { return $entry === $requestIP; } // 处理带子网掩码的情况 list($subnet, $prefix) = explode('/', $entry); // 把IP和子网都转成32位长整型,方便计算 $ipLong = ip2long($requestIP); $subnetLong = ip2long($subnet); // 根据前缀长度计算子网掩码的长整型值 $mask = -1 << (32 - (int)$prefix); // 通过按位与操作,对比IP和子网的网络部分是否一致 return ($ipLong & $mask) === ($subnetLong & $mask); }); if ($isAuthorized) { return $next($request); } abort(response('Unauthorized IP', 403));
我给你拆解下这段代码的逻辑:
- 用
some()方法代替原来的contains(),some()允许我们自定义匹配规则,只要白名单里有一个条目符合条件,就会返回true; - 对每个白名单条目做区分:
- 如果是不带
/的普通IP,直接和请求IP做精确对比; - 如果是带
/的子网格式,先拆分出子网地址和前缀长度,然后把IP和子网都转成整数(ip2long()的作用),再计算子网掩码,最后通过按位与操作,判断请求IP是不是落在这个子网里;
- 如果是不带
- 只要有一个条目匹配,就放行请求,否则返回403未授权。
另外给你提几个小建议:
- 这段代码目前只支持IPv4,如果你的API需要兼容IPv6,得调整逻辑(
ip2long()不支持IPv6),不过如果只用IPv4的话完全没问题; - 可以给子网格式的条目加个简单校验,比如确保拆分后有子网和前缀两个部分,前缀长度在0-32之间,避免非法格式导致报错;
- 之后你要把白名单存到数据库的话,这个判断逻辑可以直接复用,只需要从数据库取出所有IP/子网条目,再用同样的方式遍历判断就行。
备注:内容来源于stack exchange,提问作者Danny Connolly
相关产品推荐
相关产品推荐

