评论区显示用于IP归属地(后续和改进)

前言

前两天折腾了一下 WordPress 搭建的博客的评论区显示用户的IP归属地,上线之后还是遇到了很多的问题,这里记录一下。

遇到的问题

总结一下,有那么几个问题:

  1. IPv6 用户访问留言不显示归属地;
  2. 访问的用户修改 XFF 头部 IP 地址后,识别出来的的地址不显示;
  3. 国外用户访问不显示归属地;
  4. 没有获取用户的IP地址,不显示内容。

解决办法

IPv6 不显示问题

因为我的网站是同时支持 IPv4 和 IPv6 访问的,那么基于 IPv6 访问的用户留言归属地无法识别出来。这个问题的出现是由于我使用的是高德的 IP 查询归属地 API 服务,因为高德的 API 是不支持 IPv6 的,所以出现了这个问题,那么解决方案就是找一个支持 IPv6 解析的 API 就可以了。找了一圈,发现大部分支持 IPv6 解析的都是收费的而且价格特别的高,同时也折腾了一下使用纯真和 GeoIP 解析,但是效果都不太理想,主要是解析的不准确还有部分 IP 地址识别不出来。最后发现腾讯位置服务提供的 API 解析是支持 IPv6 的,且有一定的免费额度(个人认证:10000次/天)可以满足小网站的需求,于是解析 API 归属地的接口就换成了腾讯的 API 了。

用户修改 XFF 问题

这个问题无法避免,没办法限制用户修改 XFF,所以解决方案就只能是:

  1. 如果用户修改后的地址是正常 IP 地址,就直接解析就行了,只是解析出来的结果不一定准确,这个没办法;
  2. 如果用户修改后的地址非正常 IP 地址,比如是:私网地址、环回地址等,那就直接解析成”私网用户“标记就可以了;
  3. 如果用户修改后,没有地址了,那就直接显示”未知IP“了。

国外用户访问不显示归属地

因为我的网站禁用了国外用户访问,一开始没有发现这个问题,后来在群友的网站上发现了。这个问题的原因是由于直接提取的解析结果中的省份,但是大部分国外 IP 无法判断出来省份,所以省份内容为空了,造成了不显示。这里的解决办法就是同时提取国家和省份,如果发现国家是”中国“那么直接显示省份,如果发现国家不是”中国“,那就直接显示国家就可以了。

没有获取用户 IP 不显示内容

这个问题是由于部分用户通过特殊手段隐藏了 IP 地址,导致获取不到 IP 地址信息。获取不到地址信息会造成代码报错,导致整个页面都无法访问了,所以解决办法就是加一个判断,没有获取地址就直接显示”未知IP“。

代码

参考上一篇:

https://www.rsecc.cn/753.html

其他的内容和步骤不做修改,将 get_user_address.php 文件中的内容替换成如下代码即可:

<?php
function province($user_ip) {
    if (filter_var($user_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_IPV4)) {
        if (filter_var($user_ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
            $url = "https://apis.map.qq.com/ws/location/v1/ip?key=【腾讯位置服务API】&ip=" . $user_ip;
            $UserAgent = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506; .NET CLR 3.5.21022; .NET CLR 1.0.3705; .NET CLR 1.1.4322)';
            $curl = curl_init();
            curl_setopt($curl, CURLOPT_URL, $url);
            curl_setopt($curl, CURLOPT_HEADER, 0);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($curl, CURLOPT_ENCODING, '');
            curl_setopt($curl, CURLOPT_USERAGENT, $UserAgent);
            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
            $data = curl_exec($curl);
            $data = json_decode($data, true);
            $result = array_column($data, 'ad_info');
            $nation = array_column($result, 'nation'); // 获取国家
            $nation = array_shift($nation); // 获取国家
            $province = array_column($result, 'province'); // 获取省份
            $province = array_shift($province); // 获取省份
            $ad_info = $nation == "中国" ? $province : $nation; // 判断,如果国家为中国,则显示省,否则显示国家
            $ad_info = isset($ad_info) ? $ad_info : "未知IP";
            return $ad_info;
        } else {
            return "私网用户";
        }
    } else {
        return "未知IP";
    }
}
?>

最后

可能还有其他的问题,后续发现了再慢慢改进吧。


THE END