文章目录

    • 前言
    • 功能实现
    • 测试

前言

最近写了一个日志系统,需要通过访问的 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才是真实ipif (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)) {// 根据网卡取本机配置的IPInetAddress 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)) {// 根据网卡取本机配置的IPInetAddress inet = null;try {inet = InetAddress.getLocalHost();ip = inet.getHostAddress();} catch (UnknownHostException e) {e.printStackTrace();}}return ip;}

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

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

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

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

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

导入依赖:

<dependency><groupId>org.lionsoul</groupId><artifactId>ip2region</artifactId><version>1.7.2</version></dependency><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 {// dbif (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方法需要写入参数,设定trueconn.setDoOutput(true);// 设定post方法,默认getconn.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/