100分讨论一个关于验证IP是否在定义范围内的算法

little_fairycat 2010-02-25 04:24:33
软件允许用户定义可登录的IP范围,允许多种格式多种定义添加,比如*.*.*.*,192.168.*.*,192.168.125.*这样的格式,在数据库里的对应字段allowableIP为varchar(4000),每个IP之间用分号来分隔。
这样,用户定义的一个allowableIP也许会出现如下形式
192.168.245.*;192.168.125.*;192.168.251.254;192.168.*.*;192.*.*.*;
当用户登录的时候,软件服务端首先要验证来自客户端的IP是否在允许的范围内。
比如这个来自客户端的srcIP为192.168.251.111
1.用indexOf看一下allowableIP是否有*.*.*.*,有的话就一律通过;
2.把allowableIP分解成一个arraylist
3.解析出srcIP的首地址为192,去匹配是否有192或者*打头的字符串,如果有打头就不是192或者*的,直接剔除
3.然后解析出srcIP的第二个地址为168,去匹配第二个地址是否有168,或者*,不是的直接删除
4.然后解析出srcIP的第三个地址,去匹配是否有251,或者*,不是的直接删除
5.然后解析出srcIP的第四个地址,去匹配是否有111,或者*,不是的直接删除
6.剩下的就是匹配的值,只要这个arraylist.size()>0,就是匹配

觉得这个算法效率很一般,不知道有没有更好更快的方法?
...全文
393 28 打赏 收藏 转发到动态 举报
写回复
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
引用 15 楼 little_fairycat 的回复:
12楼的,没有考虑通配符*的情况,就没有测试

13楼的代码我使用不同数据测试了一下,是正确可用的

6楼的我测试了一下,当测试数据改成
String []testIps = new String[]{"192.168.1.1","192.168.256.1","192.168.253.1"};
的时候,应该有两个合理地址,即192.168.1.1和192.168.253.1,但是结果只返回了192.168.1.1,我对正则不大懂,不知道错在什么地方


通配符?
192.168.1.* = 192.168.1.0/24 = 192.168.1.0/255.255.255.0 = 192.168.1.0 - 192.168.1.255
这是常识
wenjjing2lianee 2010-02-26
  • 打赏
  • 举报
回复
引用 3 楼 nanman 的回复:
很简单
把IP转换为整数,判断是否在整数范围内就可以


这个可以.

正则也行.
测试下看哪种效率更好.....
xierangh 2010-02-26
  • 打赏
  • 举报
回复
上面少点了哦((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)
xierangh 2010-02-26
  • 打赏
  • 举报
回复
(2[0-4]\d|25[0-5]|[01]?\d\d?){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)
little_fairycat 2010-02-26
  • 打赏
  • 举报
回复
原来是这样啊
我要好好学习一下正则了
jianghuxiaoxiami 2010-02-26
  • 打赏
  • 举报
回复
引用 15 楼 little_fairycat 的回复:
6楼的我测试了一下,当测试数据改成
String []testIps = new String[]{"192.168.1.1","192.168.256.1","192.168.253.1"};
的时候,应该有两个合理地址,即192.168.1.1和192.168.253.1,但是结果只返回了192.168.1.1,我对正则不大懂,不知道错在什么地方

更正6楼的程序,那个匹配0-255的正则我用 [0-255]是错误的,实际上[0-255]是匹配了0-2之间的,5,5也就是能匹配0,1,2,5这4个数,因此要匹配[0-255]应该“(([0-9])|([1-9][0-9])|([1-2][0-5][0-5]))”
测试如下

public class Test {

public static void main(String []args){
String ipRule = "192.168.*.*";
String ipReg = ipRule.replace(".", "\\.").replace("*", "(([0-9])|([1-9][0-9])|([1-2][0-5][0-5]))");//把IP规则替换为正则
String []testIps = new String[]{"192.168.1.555","192.168.256.1","192.168.253.10"};
for(String tempIp:testIps){
if(tempIp.matches(ipReg)){
System.out.println("合法IP: "+tempIp);
}
}
}

}
lzlwzs04 2010-02-26
  • 打赏
  • 举报
回复
mark一下 学习学习
little_fairycat 2010-02-26
  • 打赏
  • 举报
回复
12楼的,没有考虑通配符*的情况,就没有测试

13楼的代码我使用不同数据测试了一下,是正确可用的

6楼的我测试了一下,当测试数据改成
String []testIps = new String[]{"192.168.1.1","192.168.256.1","192.168.253.1"};
的时候,应该有两个合理地址,即192.168.1.1和192.168.253.1,但是结果只返回了192.168.1.1,我对正则不大懂,不知道错在什么地方

码之魂 2010-02-26
  • 打赏
  • 举报
回复
用正则就搞定了。。。接分闪人。。。
  • 打赏
  • 举报
回复
不用那么复杂
package com.saturday.string;

public class IPMatcher {
public static void main(String[] args){
String allowIp="192.168.245.*;192.168.125.*;192.168.251.254;192.168.*.*;192.*.*.*;";
String ip="192.168.251.111";

System.out.println(isIPAllow(allowIp,ip));
}

public static boolean isIPAllow(
String allowIp,
String ip){

String
s1=ip,
s2=ip.replaceAll("\\.\\d+$", ".*"),
s3=ip.replaceAll("\\.\\d+\\.\\d+$", ".*.*"),
s4="*.*.*.*";

if(allowIp.indexOf(s1)>-1) return true;
if(allowIp.indexOf(s2)>-1) return true;
if(allowIp.indexOf(s3)>-1) return true;
if(allowIp.indexOf(s4)>-1) return true;

return false;
}
}
  • 打赏
  • 举报
回复
  //将127.0.0.1形式的IP地址转换成十进制整数,这里没有进行任何错误处理  
public static long ipToLong(String strIp) ...{
long[] ip = new long[4];
//先找到IP地址字符串中.的位置
int position1 = strIp.indexOf(".");
int position2 = strIp.indexOf(".", position1 + 1);
int position3 = strIp.indexOf(".", position2 + 1);
//将每个.之间的字符串转换成整型
ip[0] = Long.parseLong(strIp.substring(0, position1));
ip[1] = Long.parseLong(strIp.substring(position1+1, position2));
ip[2] = Long.parseLong(strIp.substring(position2+1, position3));
ip[3] = Long.parseLong(strIp.substring(position3+1));
return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3];
}

不转整数,就要考虑127.001.001.001的格式
slmvpvip 2010-02-26
  • 打赏
  • 举报
回复
引用 10 楼 py330316117 的回复:
引用 3 楼 nanman 的回复:
很简单
把IP转换为整数,判断是否在整数范围内就可以
ip是带十进制界点的,你怎么装换成整数,而且这么做很容易超过int的范围。个人认为6楼用正则很好

先转换成string,然后去掉“.”,再转换成整数。int不行就用long
py330316117 2010-02-26
  • 打赏
  • 举报
回复
引用 3 楼 nanman 的回复:
很简单
把IP转换为整数,判断是否在整数范围内就可以

ip是带十进制界点的,你怎么装换成整数,而且这么做很容易超过int的范围。个人认为6楼用正则很好
abc130314 2010-02-26
  • 打赏
  • 举报
回复
如果存 数据库的话。那你可以把 192.168.125.*,存进4个int字段,192-168-125-(-1)
然后select count(id) from table where (a1=192 or a1=-1) and (a2=163 or a2=-1) ...
(不过速度未知,有待测试)
lwhzhl 2010-02-26
  • 打赏
  • 举报
回复
顶...........
  • 打赏
  • 举报
回复
引用 24 楼 jianghuxiaoxiami 的回复:
火龙果也太有精神了,不过就为了验证IP写那么一堆是不是太繁琐了


实际上也没多少代码,IpUtil 只是一个工具类是一次工作,大多数的代码都在这里,都是一些 IP 字符串与 IP 值之间的转换。其他两个类的代码也不是很多呀。
jianghuxiaoxiami 2010-02-26
  • 打赏
  • 举报
回复
火龙果也太有精神了,不过就为了验证IP写那么一堆是不是太繁琐了
gavin_FLY 2010-02-26
  • 打赏
  • 举报
回复
jf 学习学习中..................
  • 打赏
  • 举报
回复
这样的表结构设计得就不是很好,如果改成这样的话会更好一些

1. ID              NUMBER(9)      PK
2. START_IP VARCHAR2(15)
3. END_IP VARCHAR2(15)
4. START_IP_VALUE NUMBER(10) 根据 START_IP 转成的数值
5. END_IP_VALUE NUMBER(10) 根据 END_IP 转成的数值


192.168.245.*;192.168.125.*;192.168.251.254;192.168.*.*;192.*.*.*; 存为

ID  START_IP         END_IP           START_IP_VALUE  END_IP_VALUE 
------------------------------------------------------------------
1 192.168.245.0 192.168.245.255 3232298240 3232298495
2 192.168.125.0 192.168.125.255 3232267520 3232267775
3 192.168.251.254 192.168.251.254 3232300030 3232300030
4 192.168.0.0 192.168.255.255 3232235520 3232301055
5 192.0.0.0 192.255.255.255 3221225472 3238002687


如果数据存储不能更改的话,那么可以这样做:

public class IpTest {

public static void main(String[] args) {
String ips = "192.*.*.*;192.168.125.*;192.168.251.254;192.168.245.*;192.168.*.*";
IpContent content = new IpContent(ips);
System.out.println(content.contains("193.173.0.1"));
}
}
import java.util.Arrays;
import java.util.regex.Pattern;

public class IpContent {

private final static String DEFAULT_SEPARATOR = ";";

private IpScope[] ipScopes;

public IpContent(String ips) {
this( ips, DEFAULT_SEPARATOR);
}

public IpContent(String ips, String separator) {
this( ips.split(Pattern.quote(separator)) );
}

public IpContent(String[] ipsArray) {
init(ipsArray);
}

public boolean contains(String ip) {
if(ipScopes == null || ipScopes.length == 0) {
return false;
}
long ipValue = IpUtil.ip2Number(ip);
for(int i = 0; i < ipScopes.length; i++) {
if(ipScopes[i].contains(ipValue)) {
return true;
}
}
return false;
}

private void init(String[] ipsArray) {
if(ipsArray == null || ipsArray.length == 0) {
return;
}
ipScopes = new IpScope[ipsArray.length];
for(int i = 0; i < ipsArray.length; i++) {
ipScopes[i] = new IpScope(ipsArray[i]);
}
Arrays.sort(ipScopes);
}

public String toString() {
StringBuilder sb = new StringBuilder();
for(int i = 0; i < ipScopes.length; i++) {
sb.append(ipScopes[i]).append("\n");
}
return sb.toString();
}
}
public class IpScope implements Comparable<IpScope> {

public final static String FUZZY_PLACEHOLDER = "*";
public final static String IP_MIN_VALUE = "0";
public final static String IP_MAX_VALUE = "255";

private String ip;

private long startIpValue;
private long endIpValue;

IpScope(String ip) {
this.ip = ip;
processIpValue();
}

public String getIp() {
return ip;
}
public String getStartIp() {
return IpUtil.number2Ip(startIpValue);
}
public String getEndIp() {
return IpUtil.number2Ip(endIpValue);
}
public long getStartIpValue() {
return startIpValue;
}
public long getEndIpValue() {
return endIpValue;
}

public boolean contains(long ipValue) {
return (ipValue >= startIpValue) && (ipValue <= endIpValue);
}

private long diff() {
return endIpValue - startIpValue;
}

private void processIpValue() {
String[] ips = ip.split("\\.");
startIpValue = toNumber(ips.clone(), IP_MIN_VALUE);
endIpValue = toNumber(ips.clone(), IP_MAX_VALUE);
}

private long toNumber(String[] ips, String defaultValue) {
for(int i = 0; i < ips.length; i++) {
ips[i] = ips[i].trim();
if(FUZZY_PLACEHOLDER.equals(ips[i])) {
ips[i] = defaultValue;
}
}
return IpUtil.ip2Number(ips);
}

public int compareTo(IpScope o) {
long diff = this.diff() - o.diff();
if(diff > Integer.MAX_VALUE) {
return Integer.MIN_VALUE;
}
return -(int)diff;
}

public String toString() {
return "IP pattern: " + ip + ", start: " + getStartIp() + ", end: " + getEndIp();
}
}
public class IpUtil {

/**
* 将 IP 字符串转为 long 数据
* @param ip
* @return
* @author frankiegao123
* 2010-2-26 下午03:47:14
*/
public static long ip2Number(String ip) {
String[] s = ip.split("\\.");
return ip2Number(s);
}

public static long ip2Number(String[] ips) {
long ipn = 0L;
for (int i = 0; i < ips.length; i++) {
ipn = (ipn << 8) | Long.parseLong(ips[i]);
}
return ipn;
}

/**
* 以字符串形式表示的 IP 地址
* @param number
* @return
* @author frankiegao123
* 2010-2-26 下午03:47:35
*/
public static String number2Ip(long number) {
char[] chs = new char[15];
int offset = 0;
for (int i = 1; i <= 4; i++) {
if(i > 1) {
chs[offset++] = '.';
}
int shift = (4 - i) * 8;
int n = (int) ((number & (0xff << shift)) >>> shift);
offset = putChar(chs, offset, n);
}
return new String(chs, 0, offset);
}

private static int putChar(char[] chs, int offset, int number) {
int len = length(number);
int t = len;
while(t > 0) {
chs[--t + offset] = (char)('0' + number % 10);
number /= 10;
}
return offset + len;
}

private static int length(int number) {
int len = 1;
while(number > 9) {
number /= 10;
len++;
}
return len;
}
}
shan1119 2010-02-25
  • 打赏
  • 举报
回复
3楼的方法很好。 回复内容太短了!
加载更多回复(8)
相关推荐

62,567

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告