关于JavaScript IP的校验

关于前端项目中ip类型的判断

目前主要解决的是ipv4、ipv4-ipv4、ipv4+mask、ipv6、ipv6-ipv6、ipv6+mask这几种情况。

已知的较为简单的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基本保持一致

  1. 首先进行IPV6或IPV6+mask的正则校验
  2. 当情况一与实际不匹配时,进行IPV6-IPV6的正则校验
  3. 进行IPToNumber,否则校验不通过

IPV6转为Number类型的函数需要对IPV6进行情况拆分(Item为传入的ipv6字符串,以fa:51:aacc::127.0.0.1为例)

  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格式
  2. 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进制数
  3. 判断简写的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
     }
      }
  4. 获取双冒号前(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
    }
  5. 为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的流程下来。

如存在问题,欢迎指正

以上

你可能感兴趣的:(javascript)