如何在Java中检查IP地址是否来自特定网络/网络掩码?

q43xntqr  于 2022-12-10  发布在  Java
关注(0)|答案(8)|浏览(344)

我需要确定给定的IP地址是否来自某个特殊网络,以便自动进行身份验证。

6tqwzwtp

6tqwzwtp1#

选项1:

使用spring-security-web的IpAddressMatcher,与Apache Commons Net不同,它同时支持ipv4和ipv6。

import org.springframework.security.web.util.matcher.IpAddressMatcher;
...

private void checkIpMatch() {
    matches("192.168.2.1", "192.168.2.1"); // true
    matches("192.168.2.1", "192.168.2.0/32"); // false
    matches("192.168.2.5", "192.168.2.0/24"); // true
    matches("92.168.2.1", "fe80:0:0:0:0:0:c0a8:1/120"); // false
    matches("fe80:0:0:0:0:0:c0a8:11", "fe80:0:0:0:0:0:c0a8:1/120"); // true
    matches("fe80:0:0:0:0:0:c0a8:11", "fe80:0:0:0:0:0:c0a8:1/128"); // false
    matches("fe80:0:0:0:0:0:c0a8:11", "192.168.2.0/32"); // false
}

private boolean matches(String ip, String subnet) {
    IpAddressMatcher ipAddressMatcher = new IpAddressMatcher(subnet);
    return ipAddressMatcher.matches(ip);
}

选项2(轻量级解决方案!):

  • 上一部分中的代码运行良好 *,但需要包含spring-security-web

如果您不想在项目中包含Spring框架,可以使用此类,它是Spring中original class得一个稍微修改过得版本,因此它没有非JRE依赖项.

/*
 * Copyright 2002-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * Matches a request based on IP Address or subnet mask matching against the remote
 * address.
 * <p>
 * Both IPv6 and IPv4 addresses are supported, but a matcher which is configured with an
 * IPv4 address will never match a request which returns an IPv6 address, and vice-versa.
 *
 * @author Luke Taylor
 * @since 3.0.2
 * 
 * Slightly modified by omidzk to have zero dependency to any frameworks other than the JRE.
 */
public final class IpAddressMatcher {
    private final int nMaskBits;
    private final InetAddress requiredAddress;

    /**
     * Takes a specific IP address or a range specified using the IP/Netmask (e.g.
     * 192.168.1.0/24 or 202.24.0.0/14).
     *
     * @param ipAddress the address or range of addresses from which the request must
     * come.
     */
    public IpAddressMatcher(String ipAddress) {

        if (ipAddress.indexOf('/') > 0) {
            String[] addressAndMask = ipAddress.split("/");
            ipAddress = addressAndMask[0];
            nMaskBits = Integer.parseInt(addressAndMask[1]);
        }
        else {
            nMaskBits = -1;
        }
        requiredAddress = parseAddress(ipAddress);
        assert  (requiredAddress.getAddress().length * 8 >= nMaskBits) :
                String.format("IP address %s is too short for bitmask of length %d",
                        ipAddress, nMaskBits);
    }

    public boolean matches(String address) {
        InetAddress remoteAddress = parseAddress(address);

        if (!requiredAddress.getClass().equals(remoteAddress.getClass())) {
            return false;
        }

        if (nMaskBits < 0) {
            return remoteAddress.equals(requiredAddress);
        }

        byte[] remAddr = remoteAddress.getAddress();
        byte[] reqAddr = requiredAddress.getAddress();

        int nMaskFullBytes = nMaskBits / 8;
        byte finalByte = (byte) (0xFF00 >> (nMaskBits & 0x07));

        // System.out.println("Mask is " + new sun.misc.HexDumpEncoder().encode(mask));

        for (int i = 0; i < nMaskFullBytes; i++) {
            if (remAddr[i] != reqAddr[i]) {
                return false;
            }
        }

        if (finalByte != 0) {
            return (remAddr[nMaskFullBytes] & finalByte) == (reqAddr[nMaskFullBytes] & finalByte);
        }

        return true;
    }

    private InetAddress parseAddress(String address) {
        try {
            return InetAddress.getByName(address);
        }
        catch (UnknownHostException e) {
            throw new IllegalArgumentException("Failed to parse address" + address, e);
        }
    }
}

注意:注意,使用此选项时,您有责任仔细检查license,以确保使用此代码没有违反上述许可证所规定得任何条款.(当然,我将此代码发布到Stackoverflow.com并不违法.)

voase2hg

voase2hg2#

Apache Commons Net具有org.apache.commons.net.util.SubnetUtils,似乎可以满足您的需求。看起来您执行了以下操作:

SubnetInfo subnet = (new SubnetUtils("10.10.10.0", "255.255.255.128")).getInfo();
boolean test = subnet.isInRange("10.10.10.10");

注意,正如carson指出的,Apache Commons Net有a bug,它在某些情况下会阻止它给出正确的答案。卡森建议使用SVN版本来避免这个bug。

ubof19bj

ubof19bj3#

你也可以试试

boolean inSubnet = (ip & netmask) == (subnet & netmask);

或更短

boolean inSubnet = (ip ^ subnet) & netmask == 0;
tcbh2hod

tcbh2hod4#

The open-source IPAddress Java library将以多态方式为IPv4和IPv6执行此操作,并处理子网。免责声明:我是那个图书馆的项目经理。
示例代码:

contains("10.10.20.0/30", "10.10.20.3");
contains("10.10.20.0/30", "10.10.20.5");
contains("1::/64", "1::1");
contains("1::/64", "2::1");
contains("1::3-4:5-6", "1::4:5");       
contains("1-2::/64", "2::");
contains("bla", "foo");

static void contains(String network, String address) {
    IPAddressString one = new IPAddressString(network);
    IPAddressString two = new IPAddressString(address);
    System.out.println(one +  " contains " + two + " " + one.contains(two));
}

输出量:

10.10.20.0/30 contains 10.10.20.3 true
10.10.20.0/30 contains 10.10.20.5 false
1::/64 contains 1::1 true
1::/64 contains 2::1 false
1::3-4:5-6 contains 1::4:5 true
1-2::/64 contains 2:: true
bla contains foo false
gcmastyq

gcmastyq5#

下面是一个适用于IPv4和IPv6的版本,一个带有前缀,一个带有网络掩码。

/**
 * Check if IP is within an Subnet defined by Network Address and Network Mask
 * @param  ip
 * @param  net
 * @param  mask
 * @return
 */
public static final boolean isIpInSubnet(final String ip, final String net, final int prefix) {
    try {
        final byte[] ipBin   = java.net.InetAddress.getByName(ip  ).getAddress();
        final byte[] netBin  = java.net.InetAddress.getByName(net ).getAddress();
        if(ipBin.length  != netBin.length ) return false;
        int p = prefix;
        int i = 0;
        while(p>=8) { if(ipBin[i] != netBin[i] ) return false; ++i; p-=8; }
        final int m = (65280 >> p) & 255;
        if((ipBin[i] & m) != (netBin[i]&m) ) return false;

        return true;
    } catch(final Throwable t) {
        return false;
    }
}

/**
 * Check if IP is within an Subnet defined by Network Address and Network Mask
 * @param  ip
 * @param  net
 * @param  mask
 * @return
 */
public static final boolean isIpInSubnet(final String ip, final String net, final String mask) {
    try {
        final byte[] ipBin   = java.net.InetAddress.getByName(ip  ).getAddress();
        final byte[] netBin  = java.net.InetAddress.getByName(net ).getAddress();
        final byte[] maskBin = java.net.InetAddress.getByName(mask).getAddress();
        if(ipBin.length  != netBin.length ) return false;
        if(netBin.length != maskBin.length) return false;
        for(int i = 0; i < ipBin.length; ++i) if((ipBin[i] & maskBin[i]) != (netBin[i] & maskBin[i])) return false;
        return true;
    } catch(final Throwable t) {
        return false;
    }
}
d5vmydt9

d5vmydt96#

我知道这是一个很老的问题,但我偶然发现这个当我正在寻找解决同样的问题。
有一个commons-ip-math库,我相信它做得非常好。请注意,截至2019年5月,该库还没有任何更新(可能是它已经非常成熟的库)。它在maven-central上可用
它支持使用IPv4和IPv6地址。其简要文档中有一些示例,说明如何检查地址是否在IPv4和IPv6的特定范围内
IPv4范围检查示例:

String input1 = "192.168.1.0";
        Ipv4 ipv41 = Ipv4.parse(input1);

        // Using CIDR notation to specify the networkID and netmask
        Ipv4Range range = Ipv4Range.parse("192.168.0.0/24");
        boolean result = range.contains(ipv41);
        System.out.println(result); //false

        String input2 = "192.168.0.251";
        Ipv4 ipv42 = Ipv4.parse(input2);

        // Specifying the range with a start and end.
        Ipv4 start = Ipv4.of("192.168.0.0");
        Ipv4 end = Ipv4.of("192.168.0.255");
        range = Ipv4Range.from(start).to(end);

        result = range.contains(ipv42); //true
        System.out.println(result);
erhoui1w

erhoui1w7#

如果你不需要解析花哨的字符串,也就是说你已经有了InetAddress对象,或者你有了可以输入到InetAddress.getByName()的普通字符串值形式的地址,那么Java标准库就可以完成这项工作。这在很大程度上与Peter Lawrey的答案中的逻辑相同,只是扩展到支持IPv6
这里有两个非常简短的方法可供选择,具体取决于您是否知道网络前缀长度或掩码。

/**
 * Test is the testAddressRaw is within the networkAddressRaw by specifying networkPrefixLength.
 */
public static boolean testLocal(byte [] networkAddressRaw, int networkPrefixLength, byte [] testAddressRaw)
{
    // If the addresses are of different length
    if (networkAddressRaw.length != testAddressRaw.length) {
        return false;
    }

    BitSet testAddrBits = BitSet.valueOf(testAddressRaw);

    BitSet subnetAddrBits = BitSet.valueOf(networkAddressRaw);

    // compare the addresses, mutates testAddrBits
    testAddrBits.xor(subnetAddrBits);

    // Keep 1s in the network part of the address only
    // discard any 1s in the host (after the network) part of the address
    // This would be equivalent to (testAddrBits & networkMaskBits)
    testAddrBits.clear(networkPrefixLength, networkAddressRaw.length * 8);

    // If any 1s remain then the network parts of the addresses were not the same
    return testAddrBits.cardinality() == 0;
}

/**
 * Test is the testAddressRaw is within the networkAddressRaw by specifying network mask with networkMaskRaw.
 */
public static boolean testLocal(byte [] networkAddressRaw, byte [] networkMaskRaw, byte [] testAddressRaw)
{
    if (networkAddressRaw.length != networkMaskRaw.length) {
        throw new IllegalArgumentException("Network mask length must be same as network address length");
    }
    // If the addresses are of different length
    if (networkAddressRaw.length != testAddressRaw.length) {
        return false;
    }

    BitSet testAddrBits = BitSet.valueOf(testAddressRaw);

    BitSet subnetAddrBits = BitSet.valueOf(networkAddressRaw);

    BitSet networkMaskBits = BitSet.valueOf(networkMaskRaw);

    // compare the addresses, mutates testAddrBits
    testAddrBits.xor(subnetAddrBits);

    // Keep 1s in the network part of the address only
    // discard any 1s in the host (after the network) part of the address
    testAddrBits.and(networkMaskBits);

    // If any 1s remain then the network parts of the addresses were not the same
    return testAddrBits.cardinality() == 0;
}

这里只介绍了几个方便的方法来处理InetAddress对象或String对象。

// convenience method to work with InetAddress objects directly
public static boolean testLocal(InetAddress networkAddress, InetAddress networkMask, InetAddress testAddress)
        throws UnknownHostException
{
    return testLocal(
            networkAddress.getAddress(),
            networkMask.getAddress(),
            testAddress.getAddress());
}

// convenience method to work with InetAddress objects directly
public static boolean testLocal(InetAddress networkAddress, int networkPrefixLength, InetAddress testAddress)
{
    return testLocal(
            networkAddress.getAddress(),
            networkPrefixLength,
            testAddress.getAddress());
}

// convenience method to work with Strings
public static boolean testLocal(String networkAddress, String networkMask, String testAddress)
        throws UnknownHostException
{
    return testLocal(
            InetAddress.getByName(networkAddress).getAddress(),
            InetAddress.getByName(networkMask).getAddress(),
            InetAddress.getByName(testAddress).getAddress());
}

// convenience method to work with Strings for addresses and a known network prefix length
public static boolean testLocal(String networkAddress, int networkPrefixLength, String testAddress)
        throws UnknownHostException
{
    return testLocal(
            InetAddress.getByName(networkAddress).getAddress(),
            networkPrefixLength,
            InetAddress.getByName(testAddress).getAddress());
}

最后一个main来显示这些方法的优点。这个main()方法可能需要相当新的Java版本。上面的方法应该可以在非常旧的Java版本中工作。

public static void main(String[] args)
                throws Exception
{
    {
        System.out.format("\nTest if given addresses are on same network as the running node.\n");
        InetAddress [] dests = {
                InetAddress.getByName("127.0.0.1"),
                InetAddress.getByName("127.127.255.255"),
                InetAddress.getByName("127.255.255.255"),
                InetAddress.getByName("192.168.0.1"),
                InetAddress.getByName("192.168.0.100"),
                InetAddress.getByName("192.169.0.1"),
                InetAddress.getByName("fe80:0:0:0" + ":55c:a39c:fa3a:a222"),
                InetAddress.getByName("fe80:0:0:1" + ":45c:a39c:fa3a:a222"),
                InetAddress.getByName("255.255.255.255"),
        };

        List<InterfaceAddress> nodeAddresses =
                NetworkInterface.networkInterfaces()
                .flatMap(iface -> iface.getInterfaceAddresses().stream())
                .collect(Collectors.toList())
                ;

        for (InetAddress dst: dests) {
            for (InterfaceAddress ifaceAddr: nodeAddresses) {
                boolean isLocal = testLocal(ifaceAddr.getAddress(), ifaceAddr.getNetworkPrefixLength(), dst);
                System.out.format("%-6s - DST=%s, IFACE=%s\n",
                        Boolean.toString(isLocal), dst, ifaceAddr);
            }
        }
    }

    {
        System.out.format("\nTests by network mask\n");
        String[][] testsByMask = {
                {"127.0.0.0", "255.0.0.0", "127.0.0.1"},
                {"127.0.0.0", "255.0.0.0", "127.255.0.1"},
                {"192.168.0.0", "255.255.0.0", "192.168.1.1"},
                {"192.168.0.0", "255.255.255.0", "192.168.1.1"},
        };

        for (String[] test: testsByMask) {
            InetAddress networkAddress = InetAddress.getByName(test[0]);
            InetAddress networkMask = InetAddress.getByName(test[1]);
            InetAddress testAddress = InetAddress.getByName(test[2]);
            boolean isLocal = testLocal(networkAddress, networkMask, testAddress);
            System.out.format("%-6s - DST=%s, NET=%s, MASK=%s\n",
                    Boolean.toString(isLocal), testAddress, networkAddress, networkMask);
        }
    }
}
mm5n2pyu

mm5n2pyu8#

为了检查子网中的IP地址,我在SubnetUtils类中使用了isInRange方法。但是这个方法有一个bug,如果你的子网是X,每个低于X的IP地址,isInRange都返回true。例如,如果你的子网是10.10.30.0/24,而你想检查10.10.20.5,这个方法返回true。为了处理这个bug,我使用了下面的代码。

public static void main(String[] args){
    String list = "10.10.20.0/24";
    String IP1 = "10.10.20.5";
    String IP2 = "10.10.30.5";
    SubnetUtils  subnet = new SubnetUtils(list);
    SubnetUtils.SubnetInfo subnetInfo = subnet.getInfo();
    if(MyisInRange(subnetInfo , IP1) == true)
       System.out.println("True");
    else 
       System.out.println("False");
    if(MyisInRange(subnetInfo , IP2) == true)
       System.out.println("True");
    else
       System.out.println("False");
}

private boolean MyisInRange(SubnetUtils.SubnetInfo info, String Addr )
{
    int address = info.asInteger( Addr );
    int low = info.asInteger( info.getLowAddress() );
    int high = info.asInteger( info.getHighAddress() );
    return low <= address && address <= high;
}

相关问题