获取ip属地

ip2region

引入依赖

<dependency>
     <groupId>org.lionsoul</groupId>
     <artifactId>ip2region</artifactId>
     <version>1.7.2</version>
 </dependency>
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
 </dependency>

获取ip2region.db文件,放在resources
https://gitcode.net/mirrors/lionsoul2014/ip2region/-/tree/master/v1.0/data
在这里插入图片描述
工具类

package com.example.ipdemo.utils;

import org.lionsoul.ip2region.DataBlock;
import org.lionsoul.ip2region.DbConfig;
import org.lionsoul.ip2region.DbSearcher;
import org.lionsoul.ip2region.Util;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;

public class IpUtil {

    /**
     * 获取IP地址
     * @param request
     * @return
     */

    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if ("0:0:0:0:0:0:0:1".equals(ip)) {
            ip = "127.0.0.1";
        }
        if (ip.split(",").length > 1) {
            ip = ip.split(",")[0];
        }
        return ip;
    }

    /**
     * 根据IP地址获取城市
     * @param ip
     * @return
     */
    public static String getCityInfo(String ip) {
        URL url = IpUtil.class.getClassLoader().getResource("ip2region.db");
        File file;
        if (url != null) {
            file = new File(url.getFile());
        } else {
            return null;
        }
        if (!file.exists()) {
            System.out.println("Error: Invalid ip2region.db file, filePath:" + file.getPath());
            return null;
        }
        //查询算法
        int algorithm = DbSearcher.BTREE_ALGORITHM; //B-tree
        //DbSearcher.BINARY_ALGORITHM //Binary
        //DbSearcher.MEMORY_ALGORITYM //Memory
        try {
            DbConfig config = new DbConfig();
            DbSearcher searcher = new DbSearcher(config, file.getPath());
            Method method;
            switch ( algorithm )
            {
                case DbSearcher.BTREE_ALGORITHM:
                    method = searcher.getClass().getMethod("btreeSearch", String.class);
                    break;
                case DbSearcher.BINARY_ALGORITHM:
                    method = searcher.getClass().getMethod("binarySearch", String.class);
                    break;
                case DbSearcher.MEMORY_ALGORITYM:
                    method = searcher.getClass().getMethod("memorySearch", String.class);
                    break;
                default:
                    return null;
            }
            DataBlock dataBlock;
            if (!Util.isIpAddress(ip)) {
                System.out.println("Error: Invalid ip address");
                return null;
            }
            dataBlock  = (DataBlock) method.invoke(searcher, ip);
            return dataBlock.getRegion();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

测试
在这里插入图片描述

其它
部署服务器后,resources下文件获取不到,是因为本地是目录结构获取,打成jar后,就获取不到了,得用InputStream
在这里插入图片描述

Error: Invalid ip2region.db file, filePath:file:/usr/local/download/jarPackage/ipdemo-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/ip2region.db

参考:https://blog.csdn.net/sco5282/article/details/126210398
getCityInfo方法修改后

    /**
     * 根据IP地址获取城市
     * @param ip
     * @return
     */
    public static String getCityInfo(String ip) throws IOException {
//        URL url = IpUtil.class.getClassLoader().getResource("ip2region.db");
        ClassPathResource classPathResource = new ClassPathResource("ip2region.db");
        InputStream in = classPathResource.getInputStream();
        File tmpFile = File.createTempFile("temp",".temp");
        OutputStream out=new FileOutputStream(tmpFile);
        byte[] data = new byte[1024];
        int len ;
        while ((len =in.read(data))!=-1){
            out.write(data,0,len);
        }
        in.close();
        out.close();
        File file=new File(tmpFile.getAbsolutePath());
        System.out.println("--------------------------------:"+file.getAbsolutePath());
        if (!file.exists()) {
            System.out.println("Error: Invalid ip2region.db file, filePath:" + file.getPath());
            return null;
        }
        //查询算法
        int algorithm = DbSearcher.BTREE_ALGORITHM; //B-tree
        //DbSearcher.BINARY_ALGORITHM //Binary
        //DbSearcher.MEMORY_ALGORITYM //Memory
        try {
            DbConfig config = new DbConfig();
            DbSearcher searcher = new DbSearcher(config, file.getPath());
            Method method;
            switch ( algorithm )
            {
                case DbSearcher.BTREE_ALGORITHM:
                    method = searcher.getClass().getMethod("btreeSearch", String.class);
                    break;
                case DbSearcher.BINARY_ALGORITHM:
                    method = searcher.getClass().getMethod("binarySearch", String.class);
                    break;
                case DbSearcher.MEMORY_ALGORITYM:
                    method = searcher.getClass().getMethod("memorySearch", String.class);
                    break;
                default:
                    return null;
            }
            DataBlock dataBlock;
            if (!Util.isIpAddress(ip)) {
                System.out.println("Error: Invalid ip address");
                return null;
            }
            dataBlock  = (DataBlock) method.invoke(searcher, ip);
            return dataBlock.getRegion();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            tmpFile.delete();
        }
        return null;
    }

在这里插入图片描述

获取公网ip地址

	Document doc = Jsoup.connect("http://chaipip.com/").get();
	Elements eles = doc.select("#ip");
	System.out.println(eles.attr("value"));

代码解析

这些都是用于获取客户端IP地址的方法或头部字段。它们之间的区别如下:

        /**
         * 这是一个HTTP头部字段,通常由代理服务器添加。它用于识别客户端的原始IP地址,即客户端通过的所有代理服务器的IP地址。
         * 如果客户端经过多个代理服务器,那么这个字段的值会是一个以逗号分隔的IP地址列表。需要注意的是,这个字段可以被伪造,因此不一定可信。 
         */
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            /**
             * 这也是一个HTTP头部字段,有些代理服务器会将客户端的真实IP地址添加到这个字段中。与 x-forwarded-for 类似,它也可以被伪造,因此不一定可信。 
             */
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            /**
             * 这是一个过时的HTTP头部字段,以前一些代理服务器会将客户端的IP地址添加到这个字段中。目前很少使用这个字段了。 
             */
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            /**
             * 这是一个WebLogic服务器特定的HTTP头部字段,用于获取客户端的IP地址。它也可以被伪造,不一定可信。 
             */
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            /**
             * 这是一个Java Servlet API提供的方法,用于获取客户端的IP地址。它是通过底层的网络连接获取的,不依赖于任何特定的HTTP头部字段。
             * 这个方法获取的IP地址是相对可靠的,但在某些情况下可能会获取到代理服务器的IP地址而不是客户端的真实IP地址。
             */
            ip = request.getRemoteAddr();
        }
        if ("0:0:0:0:0:0:0:1".equals(ip)) {
            ip = "127.0.0.1";
        }
        if (ip.split(",").length > 1) {
            ip = ip.split(",")[0];
        }

扩展

一下资料来源:https://article.itxueyuan.com/46m4nB

1.x-forwarded-for(简称:XFF) :

  • X-Forwarded-for:简称XFF,它代表客户端,也就是HTTP请求的真实IP,只有在通过了HTTP代理
    或者负载均衡器时才会添加该项。
产生背景
  • X-Forwarded-For是用来识别 “通过HTTP代理或负载均衡方式连接到WEB服务器的客户端” 最原始的ip地址的请求字段。
  • 服务端获取客户端ip地址的常用方法有两种:
    Remote Address
    X-Forwarded-for
  • 在java中,获取 客户端ip地址 最简单的方式就是 request.getRemoteAddr(),即第一种方式。
  • 这种方式可以直接获取到连接服务器的客户端ip(在中间没有代理的情况下,的确是最简单有效的方式)。
  • 但是当今的互联网web应用很少会将 应用服务器 直接 对外服务,有的甚至可能有多层代理。在有反向代理的情况下 直接使用 request.getRemoteAddr(),获取到的ip地址是Nginx(或其他的代理服务器的ip地址),而不是客户端的ip地址。
    • 这是因为:
      HTTP协议是基于TCP协议的,由于request.getRemoteAddr()获取到的是TCP层直连的客户端ip,对于web应用服务器来说直接连接它的客户端实际上是Nginx,也就是说TCP层是拿不到真实的客户端的ip的。
  • 为了解决上面的问题,很多 HTTP代理 会在HTTP协议头中添加 X-Forwarded-for头,用于追踪请求来源最真实的ip地址。
XFF头格式
  • 该HTTP头一般如下:
    • X-Forwarded-For:真实IP,代理1的IP,代理2的IP,代理3的IP, …
XFF头ip追加原理
  • 假设服务端收到的XFF信息为:X-Forwarded-For:IP0,IP1,IP2
    • 那么就说明:该请求成功通过了 三台 代理服务器:proxy1,proxy2,proxy3(在这里你可能会有疑问,为什么是3台代理服务器,请继续往下看)
  • 首先我们要知道,在XFF形成的过程中,代理服务器 每成功收到一个请求,就把 请求来源的IP地址 添加到右边。
    • 注意:
      1.添加的是 请求来源的IP地址
      2.是 添加ip地址 而不是 覆盖ip地址
  • 那么XFF头形成过程如下:
    • 在请求刚从client1中发出的时候,XFF是空的
    • 当请求 通过第一个代理 的时候,由于添加的是 请求来源的IP地址,所以该代理服务器会把 客户端/浏览器的IP地址(即最原始的IP地址)添加为client1;
    • 当请求 通过第二个代理 的时候,由于添加的是 请求来源的IP地址,所以该代理服务器会把 第一个代理的ip地址 追加到proxy1位置上;
    • 当请求 通过第三个代理 的时候,由于添加的是 请求来源的IP地址,所以该代理服务器会把 第二个代理的IP地址 追加到proxy2位置上;
    • 列表中之所以没有IP3,是因为proxy3直连服务器,proxy3会给XFF追加IP2,表示它是在帮proxy2转发请求,而IP3可以在服务端通过Remote Address 字段直接获得,不需要再往 XFF头 上继续追加了。
  • 从该过程便可以发现:
    由于添加的是 请求来源的IP地址,所以导致在 第一个代理处 添加 最原始的ip地址 ,在 第二个代理处添加 第一个代理的ip地址… 这样的错位的关系。
public String getClientIp(HttpServletRequest request) {
    String xff = request.getHeader("X-Forwarded-For");
  if (xff == null) {
    return request.getRemoteAddr();
  } else {
    return xff.contains(",") ? xff.split(",")[0] : xff;
  }
}
//另外,要让Nginx支持X-Forwarded-For头,需要配置:proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  • 以上就是一种常用的获取客户端真实ip的方法:
    • 首先从HTTP头中获取X-Forwarded-For,如果X-Forwarded-For头存在就按逗号分隔取最左边第一个ip的地址,不存在就直接通过request.getRemoteAddr()获取ip地址
  • 注意:在使用X-Forwarded-For获取ip时,默认获取的是最左边的值(即client1)
利用X-Forwarded-For伪造客户端ip漏洞
  • 首先,我们要知道 Remote Address 是无法伪造的,因为它是直接从TCP连接信息中获取到的,而建立TCP连接需要三次握手。如果伪造了源ip,便无法建立TCP连接,更不会有后面的HTTP请求。
  • 但是,X-Forwarded-For头是可以伪造的。一般的客户端(如:浏览器)在发送请求的时候是没有X-Forwarded-For头的,当请求到达第一个代理服务器的时候,代理服务器会加上X-Forwarded-For请求头,并将值设为客户端的ip地址(也就是最左边的第一个值),后面如果还有多个代理,则会依次将ip追加到X-Forwarded-For都的最右边,最终当请求到达web应用服务器时,应用通过获取X-Forwarded-For头取左边第一个ip即为客户端真实ip
  • 但是如果客户端在发送请求时,就在请求头上带上一个伪造的X-Forwarded-For,由于后续的每层代理只会追加而不会覆盖ip,那么最终到达应用服务器时,获取的最左边第一个ip地址将会是客户端伪造的ip。也就是上面的java代码中getClientIp()方法获取的ip地址很有可能是伪造的ip地址。
  • 该漏洞如果没有加以限制,则会造成巨大的安全隐患
  • 可以利用工具Postman或burpsuite进行XFF攻击

2.X-Real-IP :这也是一个HTTP头部字段,有些代理服务器会将客户端的真实IP地址添加到这个字段中。与 x-forwarded-for 类似,它也可以被伪造,因此不一定可信。

3.Proxy-Client-IP :这是一个过时的HTTP头部字段,以前一些代理服务器会将客户端的IP地址添加到这个字段中。目前很少使用这个字段了。

4.WL-Proxy-Client-IP :这是一个WebLogic服务器特定的HTTP头部字段,用于获取客户端的IP地址。它也可以被伪造,不一定可信。

5.request.getRemoteAddr() :这是一个Java Servlet API提供的方法,用于获取客户端的IP地址。它是通过底层的网络连接获取的,不依赖于任何特定的HTTP头部字段。这个方法获取的IP地址是相对可靠的,在中间没有代理的情况下,的确是最简单有效的方式,但是当今的互联网web应用很少会将 应用服务器 直接 对外服务,有的甚至可能有多层代理。在有反向代理的情况下 直接使用 request.getRemoteAddr(),获取到的ip地址是Nginx(或其他的代理服务器的ip地址),而不是客户端的ip地址。
这是因为:
HTTP协议是基于TCP协议的,由于request.getRemoteAddr()获取到的是TCP层直连的客户端ip,对于web应用服务器来说直接连接它的客户端实际上是Nginx,也就是说TCP层是拿不到真实的客户端的ip的。为了解决上面的问题,很多 HTTP代理 会在HTTP协议头中添加 X-Forwarded-for头,用于追踪请求来源最真实的ip地址。

总结来说, x-forwarded-for 和 X-Real-IP 是常用的获取客户端IP地址的头部字段,但它们可以被伪造,因此不一定可信。而 Proxy-Client-IP 和 WL-Proxy-Client-IP 是一些过时或特定于某些服务器的头部字段,使用较少。 request.getRemoteAddr() 是通过底层网络连接获取IP地址的方法,相对可靠。在实际使用中,应根据具体情况选择适合的方法来获取客户端的IP地址。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值