前言
项目需要根据客户的ip判断当前是否在中国大陆境内访问前端系统,实现一定的业务。
通过第三方获取接口获取客户端IP,并判断当前IP是否在中国境内(不包括香港、台湾)。
当前文章记录完整的实现流程。
本文提供一种思路,如有其它需求可参考。
前置资源获取说明
- 1、第三方接口获取IP资源
- 网站:https://www.ipify.org/更多详细使用方式自行查阅
- 当前网站也支持根据ip获取所属国家省市区等详细信息的接口,有免费付费的一些选项,但是我注册账号一直提示失败所以后来放弃了。
- 2、获取全世界各国家ip段区间数据:https://download.ip2location.com/lite/
- 上图excel中的数据就是下载的csv数据,其中包含世界各国的ip段
- 将上述excel进行筛选只保留CN的数据拷贝保存(如有需要香港台湾的自行筛选CN、HK、TW进行拷贝保存)
- 将保存后的excel的第一行添加为 start end 如下图所示
- 然后全选拷贝至剪切板,之后在https://uutool.cn/excel2json/excel转json在线网站进行转换处理
- 将数据拷贝保存到新建的js文件中等待使用
注意:ip区间段是有变化的,所以如果项目考虑到ip的实时变更问题,那就再找其它获取方式由后台定时读取进行缓存处理暴漏接口给到前端。
代码完整实现
1、引入jquery为了调用获取ip地址接口请求
2、引入上述保存的chinaIP.js文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>获取客户端ip 并判断是否在中国境内 不包含香港 台湾</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
<script src="china_ip.js"></script>
</head>
<body>
<h1 id="result"></h1>
<script>
// 参考资料
// https://www.ipify.org/
// https://geo.ipify.org/pricing
// 全世界范围内ip段:https://lite.ip2location.com/database/ip-country
// https://download.ip2location.com/lite/
/**
* https://api.ipify.org/
* https://ipv4.icanhazip.com/
* 两个地址均可获取当前访问系统的用户的ip地址,接口性能自行对比
*/
// const start1 = new Date().getTime()
// $.get("https://api.ipify.org/", function (data, status) { // 获取ip
// const diff = new Date().getTime() - start1
// document.querySelector('#ip1').innerHTML = 'ipv4:' + data + '----用时:' + diff
// })
// const start2 = new Date().getTime()
// $.get("https://ipv4.icanhazip.com/", function (data, status) {
// const diff = new Date().getTime() - start2
// document.querySelector('#ip').innerHTML = 'ipv4:' + data + '----用时:' + diff
// console.log(ipToInt(data.trim())); // 获取到的ip后缀带空格 所以需要手动去掉
// })
// 接口调用获取ip
$.get("https://ipv4.icanhazip.com/", function (ip, status) {
const ipNum = ipToInt(ip.trim()) // 将xx.xx.xx.xx的ip转换成10进制的格式数值,用作和区间段做对比
const atHome = isAtHome(chinaIp, ipNum) // 传入区间段和当前ip数值 判断返回是否查找到 找到则返回区间段的下标 否则返回-1
if (atHome === -1) {
// console.log('不在中国境内 包含香港 台湾');
document.querySelector('#result').innerHTML = 'IP: ' + ip.trim() + '</br> result:不在中国境内 包含香港 台湾'
} else {
// console.log('在中国境内 不包含香港 台湾');
document.querySelector('#result').innerHTML = 'IP: ' + ip.trim() + '</br> result:在中国境内 不包含香港 台湾'
}
})
// 循环方式 判断ip是否在中国境内4000多个区间内
// function isAtHome(ipNum) {
// console.log(chinaIp.length);
// let atHome = false // 不在国内
// // 此处查找可以使用二分法
// for (let i = 0; i < chinaIp.length; i++) {
// console.log(i);
// const ipArea = chinaIp[i]
// const s = Number(ipArea.start)
// const e = Number(ipArea.end)
// if (ipNum >= s && ipNum <= e) {
// atHome = true
// break
// }
// }
// return atHome
// }
// 二分 判断ip是否在中国境内4000多个区间内
function isAtHome (nums, target) {
let low = 0,
high = nums.length - 1; // 下标
while (low <= high) {
const mid = Math.floor((high - low) / 2) + low;
const num = nums[mid];
if (target >= Number(num.start) && target <= Number(num.end)) {
return mid;
} else if (Number(num.start) > target) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
};
// 将ip转换成10进制数字
function ipToInt (ip) {
var REG = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/;
var xH = "", result = REG.exec(ip);
if (!result) return -1;
return (parseInt(result[1]) << 24
| parseInt(result[2]) << 16
| parseInt(result[3]) << 8
| parseInt(result[4])) >>> 0;
}
</script>
</body>
</html>
最终效果
完整流程皆由前端实现,如果有更好的方式,欢迎提供。