SpringBoot整合ip2region實(shí)現(xiàn)使用ip監(jiān)控用戶訪問(wèn)城市的詳細(xì)過(guò)程
舉個(gè)栗子??
最近,多平臺(tái)都上線了展示近期發(fā)帖所在地功能,比如抖音、微博、百度,像下面那樣:


那么這個(gè)功能都是如何實(shí)現(xiàn)的呢?
一般有兩個(gè)方法:GPS 定位的信息和用戶 IP 地址。
由于每個(gè)手機(jī)都不一定會(huì)打開 GPS,而且有時(shí)并不太需要太精確的位置(到城市這個(gè)級(jí)別即可),所以根據(jù) IP 地址入手來(lái)分析用戶位置是個(gè)不錯(cuò)的選擇。
所以ip2region框架應(yīng)運(yùn)而生,GitHub上??已經(jīng)10.6K,值得一用。
GitHub地址:github.com/lionsoul201…
快速上手??
第一步,將整個(gè)項(xiàng)目down下來(lái),找到data目錄,進(jìn)入
這里有三份ip地址庫(kù),我們將ip2region.xdb復(fù)制出來(lái),等下我們的java項(xiàng)目中需要使用到。

第二步,創(chuàng)建maven項(xiàng)目,引入依賴
pom.xml依賴如下:
<!-- ip2region -->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.6.3</version>
</dependency>
<!-- 用于讀取ip2region.xdb文件使用 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>加好依賴后,在resources目錄下創(chuàng)建ip2region文件夾,把上面的ip2region.xdb文件放進(jìn)去。
第三步,編寫測(cè)試類
package com.example.springbootip.util;
import org.apache.commons.io.FileUtils;
import org.lionsoul.ip2region.xdb.Searcher;
import java.io.File;
import java.text.MessageFormat;
import java.util.Objects;
public class AddressUtil {
/**
* 當(dāng)前記錄地址的本地DB
*/
private static final String TEMP_FILE_DIR = "/home/admin/app/";
/**
* 根據(jù)IP地址查詢登錄來(lái)源
*
* @param ip
* @return
*/
public static String getCityInfo(String ip) {
try {
// 獲取當(dāng)前記錄地址位置的文件
String dbPath = Objects.requireNonNull(AddressUtil.class.getResource("/ip2region/ip2region.xdb")).getPath();
File file = new File(dbPath);
//如果當(dāng)前文件不存在,則從緩存中復(fù)制一份
if (!file.exists()) {
dbPath = TEMP_FILE_DIR + "ip.db";
System.out.println(MessageFormat.format("當(dāng)前目錄為:[{0}]", dbPath));
file = new File(dbPath);
FileUtils.copyInputStreamToFile(Objects.requireNonNull(AddressUtil.class.getClassLoader().getResourceAsStream("classpath:ip2region/ip2region.xdb")), file);
}
//創(chuàng)建查詢對(duì)象
Searcher searcher = Searcher.newWithFileOnly(dbPath);
//開始查詢
return searcher.searchByStr(ip);
} catch (Exception e) {
e.printStackTrace();
}
//默認(rèn)返回空字符串
return "";
}
public static void main(String[] args) {
System.out.println(getCityInfo("1.2.3.4"));
}
}
輸出結(jié)果如下:

項(xiàng)目實(shí)現(xiàn)??
1、思路分析
通過(guò)上面簡(jiǎn)單的例子我們已經(jīng)可以通過(guò)ip獲取地域了,那么接下來(lái)將實(shí)現(xiàn)如何監(jiān)控Controller接口的訪問(wèn)地址。
首先,在一個(gè)項(xiàng)目中肯定有很多接口,所以我們不能直接在接口中寫代碼的方式去實(shí)現(xiàn),這樣代碼復(fù)雜度、耦合度太高。所以我打算在這里使用注解切面的方式實(shí)現(xiàn),只需要在接口方法上加上 @Ip 注解就可以實(shí)現(xiàn)。不知道切面是什么的同學(xué)可以參考這篇文章:Springboot如何使用Aspectj實(shí)現(xiàn)AOP面向切面編程
其次,有些項(xiàng)目中會(huì)使用Nginx等反向代理軟件,則不能通過(guò) request.getRemoteAddr()獲取 IP地址,如果使用了多級(jí)反向代理的話,X-Forwarded-For的值并不止一個(gè),而是一串IP地址,X-Forwarded-For中第一個(gè)非 unknown的有效IP字符串,則為真實(shí)IP地址。
最后,讓我們來(lái)實(shí)現(xiàn)這個(gè)功能吧!
2、配置文件
SpringBoot項(xiàng)目pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-ip</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-ip</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- ip2region -->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.6.3</version>
</dependency>
<!-- aop切面 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3、項(xiàng)目代碼
項(xiàng)目結(jié)構(gòu)

SpringbootIpApplication.java
package com.example.springbootip;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringbootIpApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootIpApplication.class, args);
}
}TestController.java
package com.example.springbootip.controller;
import com.example.springbootip.ip2region.Ip;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/hello")
@Ip
public String hello() {
return "hello";
}
}Ip.java
package com.example.springbootip.ip2region;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Ip {
}IpAspect.java
package com.example.springbootip.ip2region;
import com.example.springbootip.util.AddressUtil;
import com.example.springbootip.util.HttpContextUtil;
import com.example.springbootip.util.IPUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
@Aspect
@Component
public class IpAspect {
@Pointcut("@annotation(com.example.springbootip.ip2region.Ip)")
public void pointcut() {
// do nothing
}
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint point) throws Throwable {
HttpServletRequest request = HttpContextUtil.getHttpServletRequest();
String ip = IPUtil.getIpAddr(request);
System.out.println(MessageFormat.format("當(dāng)前IP為:[{0}];當(dāng)前IP地址解析出來(lái)的地址為:[{1}]", ip, AddressUtil.getCityInfo(ip)));
return point.proceed();
}
}
AddressUtil.java
package com.example.springbootip.util;
import org.apache.commons.io.FileUtils;
import org.lionsoul.ip2region.xdb.Searcher;
import java.io.File;
import java.text.MessageFormat;
import java.util.Objects;
public class AddressUtil {
/**
* 當(dāng)前記錄地址的本地DB
*/
private static final String TEMP_FILE_DIR = "/home/admin/app/";
/**
* 根據(jù)IP地址查詢登錄來(lái)源
*
* @param ip
* @return
*/
public static String getCityInfo(String ip) {
try {
// 獲取當(dāng)前記錄地址位置的文件
String dbPath = Objects.requireNonNull(AddressUtil.class.getResource("/ip2region/ip2region.xdb")).getPath();
File file = new File(dbPath);
//如果當(dāng)前文件不存在,則從緩存中復(fù)制一份
if (!file.exists()) {
dbPath = TEMP_FILE_DIR + "ip.db";
System.out.println(MessageFormat.format("當(dāng)前目錄為:[{0}]", dbPath));
file = new File(dbPath);
FileUtils.copyInputStreamToFile(Objects.requireNonNull(AddressUtil.class.getClassLoader().getResourceAsStream("classpath:ip2region/ip2region.xdb")), file);
}
//創(chuàng)建查詢對(duì)象
Searcher searcher = Searcher.newWithFileOnly(dbPath);
//開始查詢
return searcher.searchByStr(ip);
} catch (Exception e) {
e.printStackTrace();
}
//默認(rèn)返回空字符串
return "";
}
public static void main(String[] args) {
System.out.println(getCityInfo("1.2.3.4"));
}
}
HttpContextUtil.java
package com.example.springbootip.util;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
/**
* @desc 全局獲取HttpServletRequest、HttpServletResponse
*/
public class HttpContextUtil {
private HttpContextUtil() {
}
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
}
public static HttpServletResponse getHttpServletResponse() {
return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();
}
}IPUtil.java
package com.example.springbootip.util;
import javax.servlet.http.HttpServletRequest;
/**
* @desc 查詢當(dāng)前訪問(wèn)的IP地址
*/
public class IPUtil {
private static final String UNKNOWN = "unknown";
protected IPUtil() {
}
/**
* 獲取 IP地址
* 使用 Nginx等反向代理軟件, 則不能通過(guò) request.getRemoteAddr()獲取 IP地址
* 如果使用了多級(jí)反向代理的話,X-Forwarded-For的值并不止一個(gè),而是一串IP地址,
* X-Forwarded-For中第一個(gè)非 unknown的有效IP字符串,則為真實(shí)IP地址
*/
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("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();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
}
}
打印結(jié)果
由于訪問(wèn)路徑是:http://127.0.0.1:8080/test/hello,所以本地解析出來(lái)的是內(nèi)網(wǎng)

到此這篇關(guān)于SpringBoot整合ip2region實(shí)現(xiàn)使用ip監(jiān)控用戶訪問(wèn)城市的文章就介紹到這了,更多相關(guān)SpringBoot整合ip2region內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)監(jiān)聽文件變化的三種方案詳解
這篇文章主要介紹了Java實(shí)現(xiàn)監(jiān)聽文件變化的三種方法,每種方案給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05
java BASE64Encoder詳細(xì)介紹及簡(jiǎn)單實(shí)例
這篇文章主要介紹了java BASE64Encoder詳細(xì)介紹及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-01-01
SpringBoot集成Lettuce客戶端操作Redis的實(shí)現(xiàn)
本文主要介紹了SpringBoot集成Lettuce客戶端操作Redis的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-11-11
jdk中動(dòng)態(tài)代理異常處理分析:UndeclaredThrowableException
最近在工作中遇到了報(bào)UndeclaredThrowableException的錯(cuò)誤,通過(guò)查找相關(guān)的資料,終于解決了,所以這篇文章主要給大家介紹了關(guān)于jdk中動(dòng)態(tài)代理異常處理分析:UndeclaredThrowableException的相關(guān)資料,需要的朋友可以參考下2018-04-04
解決Java變異出現(xiàn)錯(cuò)誤No enclosing instance of type XXX is accessible
MyEclipse+Tomcat+MAVEN+SVN項(xiàng)目完整環(huán)境搭建(圖文教程)
分享Java8中通過(guò)Stream對(duì)列表進(jìn)行去重的實(shí)現(xiàn)

