如果你和我一样有一个需求,通过访客的IP地址获得其归属地,来实现区域化的信息服务。
对于以上,我们可以通过Apanic提供的亚太地区IP数据分配情况来实现,且本文只用到IPV4,IPV6可以自行扩展。
APNIC官方IP分配表
这个地址的数据是持续更新的,所以可以定期更新这个地址的内容到你的本地。
http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest
文件格式参见:
http://ftp.apnic.net/apnic/stats/apnic/README.TXT
Format:
registry|cc|type|start|value|date|status[|extensions...]
Where:
registry The registry from which the data is taken.
For APNIC resources, this will be:
apnic
cc ISO 3166 2-letter code of the organisation to
which the allocation or assignment was made.
type Type of Internet number resource represented
in this record. One value from the set of
defined strings:
{asn,ipv4,ipv6}
start In the case of records of type 'ipv4' or
'ipv6' this is the IPv4 or IPv6 'first
address' of the range.
In the case of an 16 bit AS number, the
format is the integer value in the range:
0 - 65535
In the case of a 32 bit ASN, the value is
in the range:
0 - 4294967296
No distinction is drawn between 16 and 32
bit ASN values in the range 0 to 65535.
value In the case of IPv4 address the count of
hosts for this range. This count does not
have to represent a CIDR range.
In the case of an IPv6 address the value
will be the CIDR prefix length from the
'first address' value of <start>.
In the case of records of type 'asn' the
number is the count of AS from this start
value.
date Date on this allocation/assignment was made
by the RIR in the format:
YYYYMMDD
Where the allocation or assignment has been
transferred from another registry, this date
represents the date of first assignment or
allocation as received in from the original
RIR.
It is noted that where records do not show a
date of first assignment, this can take the
0000/00/00 value.
status Type of allocation from the set:
{allocated, assigned}
This is the allocation or assignment made by
the registry producing the file and not any
sub-assignment by other agencies.
extensions In future, this may include extra data that
is yet to be defined.
国别编码可以参照ISO 3166-2的2字母国别编码
https://zh.wikipedia.org/wiki/ISO_3166-2
我们以其中一条数据举例说明
apnic|CN|ipv4|42.192.0.0|131072|20110304|allocated
注册商|国别地区编码|类型|IP起始地址|数量|时间|分配情况
PHP实现获取不同地区的服务功能
根据IP获取国别地区
/**
* 获取IP所在地区代码
* @param $ip
* @return string
*/
function getIPAreaCode($ip)
{
$ipInt = ip2long($ip);
$apnic = "app/delegated-apnic-latest";
$handle = fopen($apnic,"r");
while(!feof($handle)){
$line = fgets($handle);
if(substr($line,0,1) == "#"){
unset($line);
continue;
}
$buffer = explode("|", $line);
if(isset($buffer[2]) && $buffer[2] == 'ipv4' && isset($buffer[4])){
$bufferIpInt = ip2long($buffer[3]);
if($bufferIpInt <= $ipInt && $ipInt <= $bufferIpInt+intval($buffer[4])){
fclose($handle);
return trim($buffer[1]);
}
}
unset($line);
unset($buffer);
}
fclose($handle);
return "未知";
}
使用方法:
禁止中国大陆访客访问
通过获取到的用户IP,判断是否来源是中国大陆,进行对应的业务处理。
if(getIPAreaCode("113.110.225.1") == "CN"){
echo "根据您所在地区(中国大陆),无法为您提供相应服务!";die;
}else{
echo "访问正常";
}
过滤中国大陆、港澳台全境访问
通过获取到的访客 IP,只检索出适合其的数据服务。以文章列表举例,
1 | Nginx从入门到精通 | Nginx.... | 0 |
---|---|---|---|
2 | Java从入门到入狱 | Java... | 2 |
字段解释:
ban_ip【0:无 1:禁大陆 2:禁中国大陆港澳台 3:禁日本 4:禁朝鲜 5:禁朝鲜和韩国】
配置文件要和数据库的字段对应,如下:
'ban_area' => [
"", // 无
"CN", // 中国大陆
["CN", "TW", "HK", "MO"], //中国大陆港澳台
["JP"], // 日本
["KP"], // 朝鲜
["KP","KR"], //朝鲜韩国
]
根据IP获取被ban区域id
/**
* 获取IP被ban区域
* @param $ip
* @return string
*/
function getIPBanArea($ip)
{
$AREA_CODE = config('app.ban_area');
$ipInt = ip2long($ip);
$apnic = storage_path("app/delegated-apnic-latest");
$handle = fopen($apnic,"r");
while(!feof($handle)){
$line = fgets($handle);
if(substr($line,0,1) == "#"){
unset($line);
continue;
}
$buffer = explode("|", $line);
if(isset($buffer[2]) && $buffer[2] == 'ipv4' && isset($buffer[4])){
$bufferIpInt = ip2long($buffer[3]);
if($bufferIpInt <= $ipInt && $ipInt <= $bufferIpInt+intval($buffer[4])){
$result = [];
foreach($AREA_CODE as $key => $codeBuffer){
if((is_array($codeBuffer) && in_array(trim($buffer[1]), $codeBuffer)) or
(is_string($codeBuffer) && $codeBuffer== trim($buffer[1]))){
$result[] = $key;
}
}
fclose($handle);
return $result;
}
}
unset($line);
unset($buffer);
}
fclose($handle);
return [];
}
根据当前IP地址,排除不适用于当地的服务,获取可用的服务。laravel使用举例 :
// 根据当前IP,获取配置中所有包含该IP的键,即地区ID。
$banAreaCode = getIPBanArea($request->getClientIp());
// 通过数据的not in 实现排除其他的服务,获取可用的服务。
$posts = Posts::where("is_private",0)->whereNotIn('ban_ip',$banAreaCode)->orderBy('created_at','DESC')->paginate(10);
判断IP地址是否同段
也可以通过这个方法判断访客的 IP 地址是否是白名单IP段的。
/**
* testMatchFilterIP("192.168.0.2", "192.168.0.0/24");
* @param $ip
* @return int
*/
function testMatchFilterIP($ip, $ipSegment)
{
echo "需要匹配的IP:$ip\n";
$ipInt = ip2long($ip);
list($ipBegin, $type) = explode('/',$ipSegment);
$ipBegin = ip2long($ipBegin);
echo "IP段起始位置:$ipBegin\n";
$mask = 0xFFFFFFFF << (32 - intval($type));
echo "掩码:$mask\n";
echo "需要匹配的IP网络地址:".long2ip($ipInt&$mask)."\n";
echo "被匹配的掩码网络地址:".long2ip($ipBegin& $mask)."\n";
echo sprintf("%s == %s\n",$ipInt&$mask,$ipBegin& $mask);
return intval($ipInt&$mask) == intval($ipBegin& $mask);
}
至此!