SpingBoot 使用GeoIP解析IP

1.设计所需实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Data
public class GeoIPInfo {
private String countryIsoCode;
private String countryName;
private String cityName;
private Double latitude;
private Double longitude;
private String timeZone;

public GeoIPInfo(String countryIsoCode, String countryName, String cityName, Double latitude, Double longitude, String timeZone) {
this.countryIsoCode = countryIsoCode;
this.countryName = countryName;
this.cityName = cityName;
this.latitude = latitude;
this.longitude = longitude;
this.timeZone = timeZone;
}
}

根据自身情况选择所需字段

2.设计工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class GeoIPUtil {
private final DatabaseReader reader;

public GeoIPUtil(String dbPath, boolean loadToMemory) {
try {
System.out.println("--------------- load GEO-IP Data ---------------");
Resource resource;
if (dbPath.startsWith("classpath:")) {
// 从classpath加载
resource = new ClassPathResource(dbPath.substring("classpath:".length()));
} else {
// 从文件系统加载
resource = new FileSystemResource(dbPath);
}
DatabaseReader.Builder builder = new DatabaseReader.Builder(resource.getFile());
if (loadToMemory) {
// 加载整个数据库到内存
builder.fileMode(com.maxmind.db.Reader.FileMode.MEMORY);
} else {
// 内存映射模式,不会将整个文件加载到内存
builder.fileMode(com.maxmind.db.Reader.FileMode.MEMORY_MAPPED);
}
this.reader = builder.build();
System.out.println("--------------- build GEO-IP Data --------------");
} catch (Exception e) {
throw new RuntimeException("Failed to initialize GeoIP database", e);
}
}
/**
* 获取IP地理信息,如果解析失败返回null
*/
public GeoIPInfo getGeoIPInfo(String ip) {
return getGeoIPInfoOptional(ip).orElse(null);
}
/**
* 获取IP地理信息,返回Optional包装
*/
public Optional<GeoIPInfo> getGeoIPInfoOptional(String ip) {
if (!StringUtils.hasText(ip)) {
return Optional.empty();
}
try {
CityResponse response = reader.city(InetAddress.getByName(ip));
return Optional.of(new GeoIPInfo(
response.getCountry().getIsoCode(),
response.getCountry().getName(),
response.getCity().getName(),
response.getLocation().getLatitude(),
response.getLocation().getLongitude(),
response.getLocation().getTimeZone()
));
} catch (Exception e) {
return Optional.empty();
}
}
}

3.配置注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(GeoIPAutoConfiguration.class)
public @interface EnableGeoIP {
/**
* mmdb数据库文件路径
*/
String value();
/**
* 是否将数据库加载到内存中
* true: 加载到内存,性能更好
* false: 每次读取文件,占用内存更少
*/
boolean loadToMemory() default true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
public class GeoIPAutoConfiguration implements ImportAware {
private AnnotationAttributes enableGeoIP;
@Bean
public GeoIPUtil geoIPUtil() {
String dbPath = this.enableGeoIP.getString("value");
boolean loadToMemory = this.enableGeoIP.getBoolean("loadToMemory");
if (dbPath.isEmpty()) {
throw new IllegalArgumentException("GeoIP database path must be specified in @EnableGeoIP annotation.");
}
return new GeoIPUtil(dbPath, loadToMemory);
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableGeoIP = AnnotationAttributes.fromMap(
importMetadata.getAnnotationAttributes(EnableGeoIP.class.getName(), false)
);
if (this.enableGeoIP == null) {
throw new IllegalArgumentException(
"@EnableGeoIP is not present on importing class " + importMetadata.getClassName());
}
}
}

4.启用注解

1
2
@EnableGeoIP("/path/GeoLite2-City.mmdb") // 绝对路径
@EnableGeoIP("classpath:/GeoLite2-City.mmdb") // classpath

两种方式启用注解

5.使用示例

1
2
3
4
5
6
7
8
9
public class Test {
private final GeoIPUtil geoIPUtil;
public Test(GeoIPUtil geoIPUtil) {
this.geoIPUtil = geoIPUtil;
}
public void getIp() {
GeoIPInfo geoIPInfo = geoIPUtil.getGeoIPInfo("8.8.8.8");
}
}