关于前端项目中ip类型的判断目前主要解决的是ipv4、ipv4ipv4、ipv4+mask、ipv6、ipv6ipv6、ipv6+mask这几种情况。已知的较为简单的ipv4全类型的判断可以借助正则和...
已知的较为简单的ipv4全类型的判断可以借助正则和ipv4转为number类型,再对两个number进行比较来判断ipv4段的格式是否正确。
IPV4+IPV4/mask的正则表达式为
^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$|^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)(\/(\d|[1-2]\d|3[0-2]))?$在判断ipv4段前首先利用正则判断格式是否正确
^((((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?))-(((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)))$之后对IPV4段进行ToNumber处理,排除127.0.0.10-172.0.0.9的情况
具体的ToNumber实现方法为
export function v4ToNumber(item) {
let num = 0;
if (item == "") {
return num;
}
const aNum = item.split(".");
if (aNum.length != 4) {
return num;
}
num += parseInt(aNum[0]) << 24;
num += parseInt(aNum[1]) << 16;
num += parseInt(aNum[2]) << 8;
num += parseInt(aNum[3]) << 0;
num >>>= 0;
return num;
}IPV6的判断情况与IPV4基本保持一致
首先进行IPV6或IPV6+mask的正则校验
当情况一与实际不匹配时,进行IPV6-IPV6的正则校验
进行IPToNumber,否则校验不通过
IPV6转为Number类型的函数需要对IPV6进行情况拆分(Item为传入的ipv6字符串,以fa:51:aacc::127.0.0.1为例)
基本的IPV6判断
const parts = items.split(":"); // parts为["fa", "51", "aacc","", "127.0.0.1"]
// IPv6 地址至少需要 2 个冒号(3 个部分)。
const _min_parts = 3
if (parts.length < _min_parts) {
return flag, res
}
// 添加处理后筛选出了基本正确的ipv6格式ipv6+ipv4格式的情况判断
if (parts[parts.length - 1].includes(".")) {
// 解决IPv6地址存在结尾为ipv4的情况
try {
const ipv4_int = v4ToNumber(parts.pop())
parts.push(Number((ipv4_int >> 16) & 0xFFFF).toString(16))
parts.push(Number(ipv4_int & 0xFFFF).toString(16))
} catch {
return res
}
}
// 此操作获取了ipv4地址,并将ipv4地址转为了16进制数判断简写的IPV6格式是否正确
let skip_index = null
for (let i = 0; i < parts.length - 1; i++) {
if (!parts[i]) {
if (skip_index) {
// 此时ipv6地址内至少存在一个'::'
return res
}
skip_index = i
}
}获取双冒号前(parts_hi)与双冒号后(parts_lo)的index值
let parts_skipped = 0
let parts_hi = 0
let parts_lo = 0
if (skip_index) {
parts_hi = skip_index
parts_lo = parts.length - skip_index - 1
if (!parts[0]) {
parts_hi--
if (parts_hi) return res
}
if (!parts[parts.length - 1]) {
parts_lo--
if (parts_lo) return res
}
parts_skipped = _HEXTET_COUNT - (parts_hi + parts_lo)
if (parts_skipped < 1) return res
} else {
if (parts.length != _HEXTET_COUNT) return res
if (!parts[0]) return res
if (!parts[parts.length - 1]) return res
parts_hi = parts.length
parts_lo = 0
parts_skipped = 0
}为IPV6地址进行ToNumber的处理
try {
let ip_int = BigInt(0)
for (let i = 0; i < parts_hi; i++) {
ip_int <<= BigInt(16)
const hextet_info = _parse_hextet(parts[i])
if (hextet_info) {
ip_int |= BigInt(hextet_info)
} else {
return res
}
}
ip_int <<= BigInt(16) * BigInt(parts_skipped);
const parts_length = parts.length;
for (let x = parts_lo; x > 0; x--) {
ip_int <<= BigInt(16)
const hextet_info = _parse_hextet(parts[parts_length - x])
if (hextet_info) {
ip_int |= BigInt(hextet_info)
} else {
return res
}
}
return ip_int
} catch {
return res
}由于将IPV6转为Number类型得出的数值较大,过程中将所有的数值处理转化为BigInt类型
其中,_parse_hextet函数是得到了每段值具体的十进制数
const _HEX_DIGITS = '0123456789ABCDEFabcdef'.split("");
function _parse_hextet(params) {
if (!(issuperlist(_HEX_DIGITS, params))) { return false }
if (params.length > 4) {
return false
}
return (parseInt(params, 16))
}
// 用来判断ipv6各个分段内是否存在不属于0~f的值
function issuperlist(target, params) {
const new_list = params.split("")
for (const i of new_list) {
const info = target.includes(i)
if (!info) return false;
}
return true
}总体来说,这些代码借鉴了python中的ipaddress库,其中对ipv6转为Number的方式整体思路就是跟着ipaddress的流程下来。