Java获取真实ip以及判断ip是否在指定范围

Posted 老周聊架构

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java获取真实ip以及判断ip是否在指定范围相关的知识,希望对你有一定的参考价值。

欢迎大家关注我的公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。

我们平时请求一些资源,对客户端和服务器的域名和ip应该再熟悉不过了;前台地址栏域名请求资源,域名会通过DNS解析器把域名解析成ip,但有时候我们后台得到不止一个ip,这是因为加了类似有反向代理功能的nginx,所以我们要得到真实的ip,自己要在后台做逻辑校验即可。

1、获取真实ip

下面来看Java获取真实ip的代码:

/**
 * 获取真实的IP
 * @param request
 * @return
 */
public static String getIp(HttpServletRequest request) 
    String ip = request.getHeader("X-Forwarded-For");
    if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) 
        //多次反向代理后会有多个ip值,第一个ip才是真实ip
        int index = ip.indexOf(",");
        if (index != -1) 
            return ip.substring(0,index);
         else 
            return ip;
        
    
    ip = request.getHeader("X-Real-IP");
    if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) 
        return ip;
    
    return request.getRemoteAddr();

通过HttpServletRequest 获取头部信息getHeader(),有两种情况,一个反向代理的,一个真实ip请求;作出相应的处理即可得到真实的ip。

2、 判断ip是否在指定范围

/**
 * 判断IP是否在指定范围
 * @param ipStart
 * @param ipEnd
 * @param ip
 * @return
 */
public static boolean ipIsValid(String ipStart,String ipEnd, String ip) 
    if (StringUtils.isEmpty(ipStart)) 
        throw new NullPointerException("起始IP不能为空!");
    
    if (StringUtils.isEmpty(ipEnd)) 
        throw new NullPointerException("结束IP不能为空!");
    
    if (StringUtils.isEmpty(ip)) 
        throw new NullPointerException("IP不能为空!");
    
    ipStart = ipStart.trim();
    ipEnd = ipEnd.trim();
    ip = ip.trim();
    final String REGX_IP = "((25[0-5]|2[0-4]\\\\d|1\\\\d2|[1-9]\\\\d|\\\\d)\\\\.)3(25[0-5]|2[0-4]\\\\d|1\\\\d2|[1-9]\\\\d|\\\\d)";
    final String REGX_IPB = REGX_IP + "\\\\-" + REGX_IP;
    if (!ipStart.matches(REGX_IP) || !ip.matches(REGX_IP) || !ipEnd.matches(REGX_IP)) 
        return false;
    
    String[] sips = ipStart.split("\\\\.");
    String[] sipe = ipEnd.split("\\\\.");
    String[] sipt = ip.split("\\\\.");
    long ips = 0L, ipe = 0L, ipt = 0L;
    for (int i = 0; i < 4; ++i) 
        ips = ips << 8 | Integer.parseInt(sips[i]);
        ipe = ipe << 8 | Integer.parseInt(sipe[i]);
        ipt = ipt << 8 | Integer.parseInt(sipt[i]);
    
    if (ips > ipe) 
        long t = ips;
        ips = ipe;
        ipe = t;
    
    return ips <= ipt && ipt <= ipe;

相信大家这段代码也都看得懂!
这里我说一下ips = ips << 8 | Integer.parseInt(sips[i]);代码,像ip我们一般不好直接比较大小,业界一般都是把它们四段不同的相应的分割出来,然后转化成整形,再通过位运算| 以及左移符号<<来得到相应的ip的整型值。

下面唠叨两句:

&| 既是逻辑运算符也是位运算符,而&&||只是逻辑运算符。

一、逻辑运算符

(1)当&与&&同为逻辑运算符时,它们都用于连接两个Boolean类型的表达式,当&和&&的两端表达式同时为真时,表达式的结果为真,只要有一端为假,那么表达式结果为假。从用法上来看,&和&&并没有什么区别,比如我们可以写两个表达式:

3>5&3>2;       3>5&&3>2;

两个运算符都可以这么用,但是不同的是,当在判断这个表达式的真或假的时候,两者的判断次数不同;

当使用&运算符: 计算机在判断表达式的值的时候,先判断3>5 的值为假,然后再判断3>2的结果为真,于是最后的结果是 假&真 为假;

但是当我们使用&&运算符的时候:计算机先判断3>5 的值为假,此时表达式的结果一定为假,所以计算机就不再往下判断了,判定表达式结果为假。

逻辑运算符&与&&的区别是:

  • & 无论左边结果是什么,右边还是继续运算;
  • &&当左边为假,右边不再进行运算。

但是两者的结果是一样的。

(2)当|||的两端表达式同时为假时,表达式的结果为假,只要有一端为真,那么表达式结果为真。

所以同理,我们可以知道|||的区别:

  • | 无论左边结果是什么,右边还是继续运算;
  • ||当左边为真,右边不再进行运算。

但是两者的结果是一样的。

所以&&和||是比较高效那么一点点。

二、&| 做位运算符,做二进制位之间的与运算:

比如:

6&3=110&011=010=2
6|3=110|011=111=7

关于java运算符更深入的了解可以看这篇博文:java运算符 与(&)、非(~)、或(|)、异或(^)

以上是关于Java获取真实ip以及判断ip是否在指定范围的主要内容,如果未能解决你的问题,请参考以下文章

判断IP地址是否在指定范围内的方法

javaweb获取客户端真实ip

javaweb获取客户端真实ip

实践使用nodejs获取用户真实IP?

Java计算一个IP地址是不是在指定范围内

powershell 判断ip地址是不是有效