首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 判斷 IP 內網/公網:私有地址段位掩碼高效檢測

     編輯:Java 相關知識

RFC 1918 私有地址段定義

CIDR範圍掩碼長度適用場景
10.0.0.0/810.0.0.0 – 10.255.255.2558A 類大型內網
172.16.0.0/12172.16.0.0 – 172.31.255.25512B 類中型內網
192.168.0.0/16192.168.0.0 – 192.168.255.25516C 類小型內網/家用路由

此外,回環地址 127.0.0.0/8、鏈路本地 169.254.0.0/16 也屬非公網。

核心算法:IPv4 轉長整型 + 位運算判斷

public final class IpUtils {
    private IpUtils() {}

    /** 判斷 IPv4 是否為私有/內網地址 */
    public static boolean isPrivateIpv4(String ip) {
        long ipNum = ipv4ToLong(ip);
        return isPrivateIpv4(ipNum);
    }

    public static boolean isPrivateIpv4(long ipNum) {
        // 10.0.0.0/8  -> 0x0A000000 - 0x0AFFFFFF
        // 172.16.0.0/12 -> 0xAC100000 - 0xAC1FFFFF
        // 192.168.0.0/16 -> 0xC0A80000 - 0xC0A8FFFF
        // 127.0.0.0/8  -> 0x7F000000 - 0x7FFFFFFF
        // 169.254.0.0/16 -> 0xA9FE0000 - 0xA9FEFFFF
        
        return (ipNum >= 0x0A000000L && ipNum <= 0x0AFFFFFFL) ||      // 10/8
               (ipNum >= 0xAC100000L && ipNum <= 0xAC1FFFFFL) ||      // 172.16/12
               (ipNum >= 0xC0A80000L && ipNum <= 0xC0A8FFFFL) ||      // 192.168/16
               (ipNum >= 0x7F000000L && ipNum <= 0x7FFFFFFFL) ||      // 127/8
               (ipNum >= 0xA9FE0000L && ipNum <= 0xA9FEFFFFL);        // 169.254/16
    }

    /** IPv4 字符串 -> 無符號長整型 (大端序) */
    public static long ipv4ToLong(String ip) {
        String[] octets = ip.split("\\.");
        if (octets.length != 4) throw new IllegalArgumentException("Invalid IPv4: " + ip);
        long num = 0;
        for (String octet : octets) {
            int val = Integer.parseInt(octet);
            if (val < 0 || val > 255) throw new IllegalArgumentException("Octet out of range: " + val);
            num = (num << 8) | val;
        }
        return num;
    }

    /** 長整型 -> IPv4 字符串 */
    public static String longToIpv4(long num) {
        return ((num >>> 24) & 0xFF) + "." +
               ((num >>> 16) & 0xFF) + "." +
               ((num >>> 8) & 0xFF) + "." +
               (num & 0xFF);
    }
}

IPv6 內網判斷(簡略版)

public static boolean isPrivateIpv6(String ip) {
    // 簡化:唯一本地地址 ULA (fc00::/7) + 鏈路本地 fe80::/10 + 回環 ::1/128
    // 完整實現建議用 InetAddress.getByName(ip).isSiteLocalAddress() / isLinkLocalAddress()
    try {
        InetAddress addr = InetAddress.getByName(ip);
        return addr.isSiteLocalAddress() || addr.isLinkLocalAddress() || addr.isLoopbackAddress();
    } catch (UnknownHostException e) {
        return false;
    }
}

Spring Boot 整合:Request 過濾器示例

@Component
public class IpAccessFilter extends OncePerRequestFilter {
    private static final Set ALLOWED_PRIVATE = Set.of("10.0.0.0/8", "192.168.0.0/16"); // 配置化

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        String clientIp = getClientIp(request); // 需處理 X-Forwarded-For, X-Real-IP
        if (IpUtils.isPrivateIpv4(clientIp) && !isWhitelisted(clientIp)) {
            response.setStatus(HttpStatus.FORBIDDEN.value());
            response.getWriter().write("內網 IP 訪問被拒絕");
            return;
        }
        chain.doFilter(request, response);
    }
    // ... getClientIp, isWhitelisted 實現略
}

性能對比:字符串匹配 vs 位運算

方法耗時 (ns/op)備註
正則/字符串 startsWith~150 ns分配對象、字符比較
InetAddress + isSiteLocalAddress~300 nsJNI 開銷、緩存查找
位運算 (本文方案)~8 ns純算術、零分配、JIT 內聯極易

總結

  • IPv4 內網判斷首選位運算,納秒級、零 GC、無依賴;
  • 注意處理 X-Forwarded-For 等代理頭,獲取真實客戶端 IP;
  • IPv6 建議直接用 InetAddress.isSiteLocalAddress(),避免手寫解析錯誤;
  • 單元測試務必覆蓋邊界:10.0.0.0、10.255.255.255、172.16.0.0、172.31.255.255、192.168.0.0、192.168.255.255。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved