如何检查 IP 地址是不是来自 Java 中的特定网络/网络掩码?
Posted
技术标签:
【中文标题】如何检查 IP 地址是不是来自 Java 中的特定网络/网络掩码?【英文标题】:How to check if an IP address is from a particular network/netmask in Java?如何检查 IP 地址是否来自 Java 中的特定网络/网络掩码? 【发布时间】:2010-10-09 07:22:39 【问题描述】:我需要确定给定的 IP 地址是否来自某个特殊网络,以便自动进行身份验证。
【问题讨论】:
【参考方案1】:选项 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,以确保您在使用此代码时没有违反上述许可规定的任何条款. (当然,我将这段代码发布到 ***.com 并不违法。)
【讨论】:
【参考方案2】: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,这会阻止它在某些情况下给出正确的答案。 Carson 建议使用 SVN 版本来避免此错误。
【讨论】:
小心使用它。有一个错误会阻止它正常工作。您可能希望将其从 SVN 中提取出来。 mail-archives.apache.org/mod_mbox/commons-issues/200902.mbox/… @carson:感谢您的警告。我编辑了我的答案以包含这些信息。 这个好像也不支持 IPv6【参考方案3】:你也可以试试
boolean inSubnet = (ip & netmask) == (subnet & netmask);
或更短
boolean inSubnet = (ip ^ subnet) & netmask == 0;
【讨论】:
ip 和网络掩码是整数还是长整数? 32 位地址 IPv4 是整数。我怀疑 IPv6 是 64 位值,但我自己没用过 hem IPv6 是 128 位值(16 字节),因此它们不能存储在单个 long 中【参考方案4】: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
【讨论】:
优秀的图书馆!我真的很喜欢它的易用性,并且它在绝大多数情况下都有效。但是,对于@Omid 的回复中所述的 IPv5 案例,它不能正常工作。能否请您合并该功能? 差异是故意的,只是解释和库实现的差异,请参阅github.com/seancfoley/IPAddress/issues/40【参考方案5】:这是一个适用于 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;
【讨论】:
【参考方案6】:我知道这是一个非常古老的问题,但是当我想解决同样的问题时,我偶然发现了这个问题。
我相信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);
【讨论】:
【参考方案7】:为了检查子网中的 IP,我使用了 SubnetUtils 类中的 isInRange 方法。但是这种方法有一个错误,如果您的子网是 X,那么每个低于 X 的 IP 地址,isInRange 都会返回 true。例如,如果您的子网是 10.10.30.0/24,并且您想检查 10.10.20.5,则此方法返回 true。为了处理这个错误,我使用了下面的代码。
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;
【讨论】:
以上是关于如何检查 IP 地址是不是来自 Java 中的特定网络/网络掩码?的主要内容,如果未能解决你的问题,请参考以下文章
PHP:我如何通过他/她的 IP 地址确定某人是不是来自美国?