如何检查 IP 地址是否来自 Java 中的特定网络/网络掩码?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/577363/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to check if an IP address is from a particular network/netmask in Java?
提问by miceuz
I need to determine if given IP address is from some special network in order to authenticate automatically.
我需要确定给定的 IP 地址是否来自某个特殊网络,以便自动进行身份验证。
采纳答案by Eddie
Apache Commons Nethas org.apache.commons.net.util.SubnetUtils
that appears to satisfy your needs. It looks like you do something like this:
Apache的百科全书网有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");
Note, as carsonpoints out, that Apache Commons Net has a bugthat prevents it from giving the correct answer in some cases. Carson suggests using the SVN version to avoid this bug.
请注意,正如卡森指出的那样,Apache Commons Net 有一个错误,在某些情况下会阻止它给出正确的答案。Carson 建议使用 SVN 版本来避免此错误。
回答by Peter Lawrey
You can also try
你也可以试试
boolean inSubnet = (ip & netmask) == (subnet & netmask);
or shorter
或更短
boolean inSubnet = (ip ^ subnet) & netmask == 0;
回答by Omid
Option 1:
选项1:
Use spring-security-web
's IpAddressMatcher. Unlike Apache Commons Net, it supports both ipv4 and ipv6.
使用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);
}
Option 2 (a lightweight solution!):
选项 2(轻量级解决方案!):
The code in previous part works perfectly finebut it needs spring-security-web
to be included.
上一部分中的代码运行良好,但需要spring-security-web
包含在内。
If you are not willing to include Spring framework in your project, you may use this class which is a slightly modified version of the original classfrom Spring, so that it has no non-JDK dependencies.
如果你不愿意在你的项目中包含 Spring 框架,你可以使用这个类,它是Spring原始类的稍微修改版本,因此它没有非 JDK 依赖项。
/*
* 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 JDK.
*/
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);
}
}
}
NOTICE: Notice that for using this option, it's your responsibility to carefully examine the licenseto make sure by using this code, you are not in violation of any terms mandated by the aforementioned license. (Of course publishing this code to Stackoverflow.com by me is not a violation.)
注意:请注意,使用此选项时,您有责任仔细检查许可证以确保使用此代码不会违反上述许可证规定的任何条款。(当然,我将此代码发布到 Stackoverflow.com 并不违规。)
回答by Hossein
To check An IP in a subnet, I used isInRange method in SubnetUtils class. But this method have a bug that if your subnet was X, every IP address that lower than X, isInRange return true. For example if your subnet was 10.10.30.0/24 and you want to check 10.10.20.5, this method return true. To deal with this bug I used below code.
为了检查子网中的 IP,我在 SubnetUtils 类中使用了 isInRange 方法。但是这个方法有一个bug,如果你的子网是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;
}
回答by Sean F
The open-source IPAddress Java librarywill do this in a polymorphic manner for both IPv4 and IPv6 and handles subnets. Disclaimer: I am the project manager of that library.
开源 IPAddress Java 库将以多态方式为 IPv4 和 IPv6 执行此操作并处理子网。免责声明:我是那个图书馆的项目经理。
Example code:
示例代码:
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));
}
Output:
输出:
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
回答by SkateScout
here is an Version that works with IPv4 and IPv6 one with Prefix and one with Network Mask.
这是一个适用于 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;
}
}
回答by railomaya
I know this is very old question, but I stumbled upon this when I was looking to solve the same problem.
我知道这是一个很老的问题,但是当我想解决同样的问题时偶然发现了这个问题。
There is commons-ip-mathlibrary that I believe does a very good job. Please note that as of May 2019, there hasn't been any updates to the library (Could be that its already very mature library). Its available on maven-central
我相信有一个commons-ip-math库做得很好。请注意,截至 2019 年 5 月,该库还没有任何更新(可能是它已经非常成熟的库)。它在maven-central上可用
It supports working with both IPv4 and IPv6 addresses. Their brief documentation has examples on how you can check if an address is in a specific range for IPv4and IPv6
它支持使用 IPv4 和 IPv6 地址。他们的简短文档包含有关如何检查地址是否在IPv4和IPv6的特定范围内的示例
Example for IPv4 range checking:
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);