Java-通过IP获取真实地址

文章目录

    • 前言
    • 功能实现
    • 测试

前言

最近写了一个日志系统,需要通过访问的 IP 地址来获取真实的地址,并且存到数据库中,我也是在网上看了一些文章,遂即整理了一下供大家参考。


功能实现

这个是获取正确 IP 地址的方法,可以直接使用的。


    public static final String UNKNOWN = "unknown";

    public static final String X_FORWARDED_FOR = "x-forwarded-for";

    public static final String PROXY_CLIENT_IP = "Proxy-Client-IP";

    public static final String WL_PROXY_CLIENT_IP = "WL-Proxy-Client-IP";

    public static final String HTTP_CLIENT_IP = "HTTP_CLIENT_IP";

    public static final String HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR";

    public static final String X_REAL_IP = "X-Real-IP";

    public static final String LOCAL_IP_V4 = "127.0.0.1";

    public static final String LOCAL_IP_V6 = "0:0:0:0:0:0:0:1";

    public static final String CDN_SRC_IP = "cdn-src-ip";

    /**
     * 获取 IP 地址
     */
    public static String getIpAddress(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String ip = headers.getFirst(X_FORWARDED_FOR);
        if (ip != null && ip.length() != 0 && !UNKNOWN.equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值,第一个ip才是真实ip
            if (ip.contains(",")) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = headers.getFirst(PROXY_CLIENT_IP);
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = headers.getFirst(WL_PROXY_CLIENT_IP);
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = headers.getFirst(HTTP_CLIENT_IP);
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = headers.getFirst(HTTP_X_FORWARDED_FOR);
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = headers.getFirst(X_REAL_IP);
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = Objects.requireNonNull(request.getRemoteAddress()).getAddress().getHostAddress();
            if (ip.equals(LOCAL_IP_V4) || ip.equals(LOCAL_IP_V6)) {
                // 根据网卡取本机配置的IP
                InetAddress inet = null;
                try {
                    inet = InetAddress.getLocalHost();
                    ip = inet.getHostAddress();
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }
            }
        }
        return ip;
    }

    /**
     * 获取请求IP.
     * @return String ip
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ip = null;
        Enumeration<?> enu = request.getHeaderNames();
        while (enu.hasMoreElements()) {
            String name = (String) enu.nextElement();
            if (CDN_SRC_IP.equalsIgnoreCase(name)
                    || X_FORWARDED_FOR.equalsIgnoreCase(name)
                    || PROXY_CLIENT_IP.equalsIgnoreCase(name)
                    || WL_PROXY_CLIENT_IP.equalsIgnoreCase(name)
                    || X_REAL_IP.equalsIgnoreCase(name)) {
                ip = request.getHeader(name);
            }
            if (org.apache.commons.lang3.StringUtils.isNotBlank(ip)){
                break;
            }
        }
        if (StringUtils.isBlank(ip)){
            ip = request.getRemoteAddr();
        }

        if (ip.equals(LOCAL_IP_V4) || ip.equals(LOCAL_IP_V6)) {
            // 根据网卡取本机配置的IP
            InetAddress inet = null;
            try {
                inet = InetAddress.getLocalHost();
                ip = inet.getHostAddress();
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
        }
        return ip;
    }

通过以上方法你可以获取到访问者的 IP 地址,只有获取到了 IP 地址,才能够拿到真实的地址

通过 IP 获取真实地址的方法有很多种,这里我只说两个:

  • 方式一:离线 IP 地址定位查询
  • 方式二:在线 IP 地址定位查询

方式一 我们需要在本地保存一个 IP 地址定位的文档,方式二就是通过 http 请求的方式访问一些可以查询 IP 地址所属地理位置的网址。

方式一就是查询比较快,但是有些地址可能查询不到,方式二就是基本上都能查到,但是要建立请求,会有点慢,所以将二者结合起来就行了。

导入依赖:

		<!-- 离线IP地址定位库 -->
        <dependency>
            <groupId>org.lionsoul</groupId>
            <artifactId>ip2region</artifactId>
            <version>1.7.2</version>
        </dependency>
        
        <!-- ali地域 -->
        <dependency>
            <groupId>com.maxmind.geoip2</groupId>
            <artifactId>geoip2</artifactId>
            <version>2.6.0</version>
        </dependency>

添加离线 IP 地址库:

src/main/resources下新建 ip2region 复制文件 ip2region.db 到目录下

------------------ ip2region.db 资源下载 -----------------
链接:百度网盘
提取码:v7se
---------------------------------------------------------

添加工具类:

RegionUtil.java

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Objects;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.lionsoul.ip2region.DataBlock;
import org.lionsoul.ip2region.DbConfig;
import org.lionsoul.ip2region.DbSearcher;
import org.lionsoul.ip2region.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;

/**
 * 根据ip离线查询地址
 *
 * @author ruoyi
 */
public class RegionUtil {
    private static final Logger log = LoggerFactory.getLogger(RegionUtil.class);

    private static final String JAVA_TEMP_DIR = "java.io.tmpdir";

    static DbConfig config = null;
    static DbSearcher searcher = null;

    // 初始化IP库
    static {
        try {
            // 因为jar无法读取文件,复制创建临时文件
            String dbPath = Objects.requireNonNull(RegionUtil.class.getResource("/ip2region/ip2region.db")).getPath();
            File file = new File(dbPath);
            if (!file.exists()) {
                String tmpDir = System.getProperties().getProperty(JAVA_TEMP_DIR);
                dbPath = tmpDir + "ip2region.db";
                file = new File(dbPath);
                ClassPathResource cpr = new ClassPathResource("ip2region" + File.separator + "ip2region.db");
                InputStream resourceAsStream = cpr.getInputStream();
                FileUtils.copyInputStreamToFile(resourceAsStream, file);
            }
            config = new DbConfig();
            searcher = new DbSearcher(config, dbPath);
            log.info("bean [{}]", config);
            log.info("bean [{}]", searcher);
        } catch (Exception e) {
            log.error("init ip region error:{}", e.toString());
        }
    }

    /**
     * 解析IP
     *
     * @param ip
     * @return
     */
    public static String getRegion(String ip) {
        try {
            // db
            if (searcher == null || StringUtils.isEmpty(ip)) {
                log.error("DbSearcher is null");
                return StringUtils.EMPTY;
            }
            long startTime = System.currentTimeMillis();
            // 查询算法
            Method method = searcher.getClass().getMethod("memorySearch", String.class);

            DataBlock dataBlock = null;
            if (!Util.isIpAddress(ip)) {
                log.warn("warning: Invalid ip address");
            }
            dataBlock = (DataBlock) method.invoke(searcher, ip);
            String result = dataBlock.getRegion();
            long endTime = System.currentTimeMillis();
            log.debug("region use time[{}] result[{}]", endTime - startTime, result);
            return result;

        } catch (Exception e) {
            log.error("error:{}", e.toString());
        }
        return StringUtils.EMPTY;
    }

}

PureNetUtils.java

package com.mike.common.core.utils;

import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * 网络访问工具类
 */
public class PureNetUtils {

    private static final Logger log = LoggerFactory.getLogger(PureNetUtils.class);

    /**
     * get方法直接调用post方法
     *
     * @param url 网络地址
     * @return 返回网络数据
     */
    public static String get(String url) {
        return post(url, null);
    }

    /**
     * 设定post方法获取网络资源,如果参数为null,实际上设定为get方法
     *
     * @param url   网络地址
     * @param param 请求参数键值对
     * @return 返回读取数据
     */
    public static String post(String url, Map<String, String> param) {
        HttpURLConnection conn = null;
        try {
            URL u = new URL(url);
            conn = (HttpURLConnection) u.openConnection();
            StringBuilder sb = null;
            if (param != null) {// 如果请求参数不为空
                sb = new StringBuilder();
                /*
                 * A URL connection can be used for input and/or output. Set the
                 * DoOutput flag to true if you intend to use the URL connection
                 * for output, false if not. The default is false.
                 */
                // 默认为false,post方法需要写入参数,设定true
                conn.setDoOutput(true);
                // 设定post方法,默认get
                conn.setRequestMethod("POST");
                // 获得输出流
                OutputStream out = conn.getOutputStream();
                // 对输出流封装成高级输出流
                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
                // 将参数封装成键值对的形式
                for (Map.Entry<String, String> s : param.entrySet()) {
                    sb.append(s.getKey()).append("=").append(s.getValue()).append("&");
                }
                // 将参数通过输出流写入
                writer.write(sb.deleteCharAt(sb.toString().length() - 1).toString());
                writer.close();// 一定要关闭,不然可能出现参数不全的错误
                sb = null;
            }
            conn.connect();// 建立连接
            sb = new StringBuilder();
            // 获取连接状态码
            int recode = conn.getResponseCode();
            BufferedReader reader = null;
            if (recode == 200) {
                // Returns an input stream that reads from this open connection
                // 从连接中获取输入流
                InputStream in = conn.getInputStream();
                // 对输入流进行封装
                reader = new BufferedReader(new InputStreamReader(in));
                String str = null;
                sb = new StringBuilder();
                // 从输入流中读取数据
                while ((str = reader.readLine()) != null) {
                    sb.append(str).append(System.getProperty("line.separator"));
                }
                // 关闭输入流
                reader.close();
                if (sb.toString().length() == 0) {
                    return null;
                }
                return sb.toString().substring(0,
                        sb.toString().length() - System.getProperty("line.separator").length());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (conn != null)// 关闭连接
                conn.disconnect();
        }
        return null;
    }

    /**
     * 获取 ip 所属地址
     *
     * @param ip IP 地址
     * @return 所属地址
     */
    public static String getAlibaba(String ip) {
        Map<String, String> map = new HashMap<>();
        map.put("ip", ip);
        map.put("accessKey", "alibaba-inc");
        String result = PureNetUtils.post("http://ip.taobao.com/outGetIpInfo", map);
        log.info("{} => POST: http://ip.taobao.com/outGetIpInfo || result: {}", ip, result);
        String address = null;
        if (StringUtils.isNotBlank(result)) {
            JSONObject jsonObject = JSONObject.parseObject(result);
            // 请求成功,解析响应数据
            if ("query success".equals(jsonObject.get("msg"))) {
                JSONObject dataMap = JSONObject.parseObject(jsonObject.getString("data"));
                String country = dataMap.getString("country");
                String region = dataMap.getString("region");
                String city = dataMap.getString("city");
                address = country + region + city;
            }
        }
        return address;
    }

}

AddressUtils .java

import com.mike.common.core.utils.PureNetUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 获取地址类
 *
 * @author ruoyi
 */
public class AddressUtils {
    private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);

    // 未知地址
    public static final String UNKNOWN = "未知地址";

    public static String getRealAddress(String ip) {
        // 内网不查询
        if (internalIp(ip)) {
            return "内网IP";
        }
        try {
            String rspStr = RegionUtil.getRegion(ip);
            if (StringUtils.isNotEmpty(rspStr)) {
                String[] obj = rspStr.split("\\|");
                String region = obj[2];
                String city = obj[3];

                return String.format("%s%s", region, city);
            }
        } catch (Exception e) {
            log.error("获取地理位置异常 {}", e.toString());
        }
        // ali地域查询
        return PureNetUtils.getAlibaba(ip);
    }

    /* 判断是否是内网IP */
    public static boolean internalIp(String ipAddress) {
        boolean isInnerIp = false;
        long ipNum = getIpNum(ipAddress);
        /*
         * 私有IP:A类 10.0.0.0-10.255.255.255 B类 172.16.0.0-172.31.255.255 C类
         * 192.168.0.0-192.168.255.255 当然,还有127这个网段是环回地址
         */
        long aBegin = getIpNum("10.0.0.0");
        long aEnd = getIpNum("10.255.255.255");
        long bBegin = getIpNum("172.16.0.0");
        long bEnd = getIpNum("172.31.255.255");
        long cBegin = getIpNum("192.168.0.0");
        long cEnd = getIpNum("192.168.255.255");
        isInnerIp = isInner(ipNum, aBegin, aEnd)
                || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd)
                || ipAddress.equals("127.0.0.1");
        return isInnerIp;
    }

    /* 获取IP数 */
    private static long getIpNum(String ipAddress) {
        String[] ip = ipAddress.split("\\.");
        long a = Integer.parseInt(ip[0]);
        long b = Integer.parseInt(ip[1]);
        long c = Integer.parseInt(ip[2]);
        long d = Integer.parseInt(ip[3]);
        return a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
    }

    private static boolean isInner(long userIp, long begin, long end) {
        return (userIp >= begin) && (userIp <= end);
    }

    public static void main(String[] args) {
        String realAddress = getRealAddress("117.136.79.113");
        System.out.println("realAddress = " + realAddress);
    }
}
           String region = dataMap.getString("region");
                String city = dataMap.getString("city");
                address = country + region + city;
            }
        }
        return address;
    }

    /* 获取IP数 */
    private static long getIpNum(String ipAddress) {
        String[] ip = ipAddress.split("\\.");
        long a = Integer.parseInt(ip[0]);
        long b = Integer.parseInt(ip[1]);
        long c = Integer.parseInt(ip[2]);
        long d = Integer.parseInt(ip[3]);
        return a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
    }

    private static boolean isInner(long userIp, long begin, long end) {
        return (userIp >= begin) && (userIp <= end);
    }

    public static void main(String[] args) {
        String realAddress = getRealAddress("117.136.79.113");
        System.out.println("realAddress = " + realAddress);
    }
}


测试

运行 AddressUtils.javamain 方法测试

在这里插入图片描述


参考博客:
Java如何通过IP获得真实地址:https://blog.csdn.net/qq_39486119/article/details/107857455
集成ip2region实现离线IP地址定位:http://doc.ruoyi.vip/ruoyi/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/37660.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【css】用css样式快速写右上角badge徽标,颜色设置为渐变色

先看效果展示&#xff0c;已公开显示在图片卡片的右上角。 首先是dom代码&#xff1a;需要两个view或者div&#xff0c;public-badge是“已公开”那个矩形&#xff0c;show-signal是右边那个下三角&#xff0c;也就是阴影部分&#xff0c;这样看起来比较有立体感。 <view…

LabVIEW-实现波形发生器

一、题目 用两种方法实现一种多类型信号波形发生器&#xff08;至少包括&#xff1a;正弦波、三角波、方波等&#xff09;&#xff0c;可以调节信号频率、幅度、相位等参数&#xff0c;可以图形化显示信号波形。 需要给出产生信号波形的基本方法、程序设计基本方法以及程序实现…

云计算的学习(二)

二、计算虚拟化 1.计算虚拟化的介绍 1.1虚拟化简介 a.什么是虚拟化 将物理设备逻辑化&#xff0c;转化成文件或者文件夹&#xff0c;这个文件或文件夹一定包含两个部分&#xff1a;一部分用于记录设备配置信息&#xff0c;另一部分记录用户数据。 虚拟机摆脱了服务器的禁锢…

性能测试工具 Jmeter 测试 JMS (Java Message Service)/ActiveMQ 性能

目录 前言 ActiveMQ 介绍 准备工作 编写jndi.properties添加到ApacheJMeter.jar 中 下载 ActiveMQ 配置 Jmeter 进行测试 点对点 (Queues 队列) 配置 Jmeter 进行测试 发布/订阅 (Topic 队列) 配置发布 Publisher 配置订阅 Subscriber 总结 前言 JMeter是一个功能强大…

全方位对比 Postgres 和 MySQL (2023 版)

根据 2023 年 Stack Overflow 调研&#xff0c;Postgres 已经取代 MySQL 成为最受敬仰和渴望的数据库。 随着 Postgres 的发展势头愈发强劲&#xff0c;在 Postgres 和 MySQL 之间做选择变得更难了。 如果看安装数量&#xff0c;MySQL 可能仍是全球最大的开源数据库。 Postgre…

Windows环境下安装Nacos

文章目录 一、什么是Nacos1. 主要特点&#xff1a;1.1 服务发现和注册&#xff1a;1.2 配置管理&#xff1a;1.3 服务管理&#xff1a;1.4 多语言支持&#xff1a;1.5 高可用性和扩展性&#xff1a; 二、Windows下安装单机版Nacos1. 安装包下载&#xff1a;2. 目录文件说明&…

冒泡排序模拟实现qsort()函数

冒泡排序模拟实现qsort函数 前言1. 分析2. 解决一&#xff0c;如何接受不同数据3. 解决二&#xff0c;如何实现不同数据的比较4. 解决三&#xff0c;如何实现不同数据交换5. 模拟bubble_sort&#xff08;&#xff09;函数排序整型所有代码实现6. 结构体排序实现7. 结尾 前言 要…

Android Studio无法打开问题解决记录

目录 1 问题起因2 发现问题3 解决问题 1 问题起因 问题的起因是我为了运行一个Kotlin项目&#xff0c;但是报了一个错误&#xff1a; Kotlin报错The binary version of its metadata is 1.5.1, expected version is 1.1.16 然后我就上百度去搜了以下&#xff0c;一篇博客让禁用…

http-server 的安装与使用

文章目录 问题背景http-server简介安装nodejs安装http-server开启http服务http-server参数 问题背景 打开一个文档默认使用file协议打开&#xff0c;不能发送ajax请求&#xff0c;只能使用http协议才能请求资源&#xff0c;所以此时我们需要在本地建立一个http服务&#xff0c…

低代码在边缘计算工业软件中的应用

近年来&#xff0c;边缘计算给工业现场带来了许多新的变化。由于计算、储存能力的大幅提升&#xff0c;边缘计算时代的新设备往往能够胜任多个复杂任务。另外&#xff0c;随着网络能力的提升&#xff0c;边缘设备与设备之间、边缘设备与工业互联网云平台之间的通讯延迟与带宽都…

Android手写占位式插件化框架之Activity通信、Service通信和BroadcastReceiver通信

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂&#xff0c;风趣幽默"&#xff0c;感觉非常有意思,忍不住分享一下给大家。 &#x1f449;点击跳转到教程 前言&#xff1a; 1、什么是插件化&#xff1f; 能运行的宿主APP去加载没有下载的APK文件…

SAP从放弃到入门系列之生产订单拆分

文章目录 一、概述二、订单拆分功能前世今生三、订单拆分不同版本的差异3.1 版本 603 以下的订单拆分3.2 自604 起版本的订单拆分 四、订单拆分实例4.1 数据准备4.2 拆分操作-到仓库的分解&#xff08;SPLT_OS&#xff09;4.2 拆分操作-到其他物料的分解&#xff08;SPLT_DP&am…

【STM32MP135】修改10.1寸屏1280x800分辨率配置,解决fb_size过小导致运行崩溃

文件路径&#xff1a;u-boot-stm32mp-v2021.10-stm32mp1-r1/configs/stm32mp13_defconfig

基于深度学习的高精度工人安全帽检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度工人安全帽检测识别系统可用于日常生活中或野外来检测与定位工人安全帽目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的工人安全帽目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用…

使用Dreambooth LoRA微调SDXL 0.9

本文将介绍如何通过LoRA对Stable Diffusion XL 0.9进行Dreambooth微调。DreamBooth是一种仅使用几张图像(大约3-5张)来个性化文本到图像模型的方法。 本教程基于通过LoRA进行Unet微调&#xff0c;而不是进行全部的训练。LoRA是在LoRA: Low-Rank Adaptation of Large Language …

2023届网络安全岗秋招面试题及面试经验分享

Hello&#xff0c;各位小伙伴&#xff0c;我作为一名网络安全工程师曾经在秋招中斩获&#x1f51f;个offer&#x1f33c;&#xff0c;并在国内知名互联网公司任职过的职场老油条&#xff0c;希望可以将我的面试的网络安全大厂面试题和好运分享给大家~ 转眼2023年秋招已经到了金…

Python应用实例(一)外星人入侵(八)

外星人入侵&#xff08;八&#xff09; 1.添加Play按钮1.1 创建Button类1.2 在屏幕上绘制按钮1.3 开始游戏1.4 重置游戏1.5 将play按钮切换到非活动状态1.6 隐藏鼠标光标 我们添加一个Play按钮&#xff0c;用于根据需要启动游戏以及在游戏结束后重启游戏&#xff0c;还会修改这…

剖析C语言字符串函数(超全)

目录 前言&#xff1a; 一、strlen函数 功能&#xff1a; 参数和返回值&#xff1a; 注意事项&#xff1a; 返回值是无符号的易错点&#xff1a; strlen函数的模拟实现 1、计数器算法 2、递归算法 3、指针减去指针 二、strcpy函数 功能&#xff1a; 参数和返回值 …

124、仿真-基于51单片机智能电表系统设计(Proteus仿真+程序+原理图+配套资料等)

方案选择 单片机的选择 方案一&#xff1a;STM32系列单片机控制&#xff0c;该型号单片机为LQFP44封装&#xff0c;内部资源足够用于本次设计。STM32F103系列芯片最高工作频率可达72MHZ&#xff0c;在存储器的01等等待周期仿真时可达到1.25Mip/MHZ(Dhrystone2.1)。内部128k字节…

盖子的c++小课堂——第十八讲:栈

目录 前言 栈的定义 栈&#xff0c;是什么&#xff1f; 例1-弹夹 问题 例2-停车场 问题 栈的概念 空栈 进栈、出栈 特点 例题 车厢调度 如何操作 数组模拟栈 入栈 出栈 栈的基本操作 判断空栈 求栈的元素数量 读栈顶元素 总结 前言 OK呀&#xff0c;说到做…