Преглед изворни кода

ip2location support ip2region & GeoLite2

MaxKey пре 1 година
родитељ
комит
2dda707584
21 измењених фајлова са 899 додато и 0 уклоњено
  1. 12 0
      maxkey-authentications/maxkey-authentication-ip2location/build.gradle
  2. 141 0
      maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/autoconfigure/IpLocationAutoConfiguration.java
  3. 50 0
      maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/AbstractIpLocation.java
  4. 39 0
      maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/IpLocation.java
  5. 35 0
      maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/IpLocationLocal.java
  6. 73 0
      maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/IpLocationParser.java
  7. 111 0
      maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/Region.java
  8. 57 0
      maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/offline/GeoIP2V4.java
  9. 61 0
      maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/offline/Ip2regionV2.java
  10. 61 0
      maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/online/Ip138.java
  11. 81 0
      maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/online/Ip138Response.java
  12. 1 0
      maxkey-authentications/maxkey-authentication-ip2location/src/main/resources/geoip2/COPYRIGHT.txt
  13. BIN
      maxkey-authentications/maxkey-authentication-ip2location/src/main/resources/geoip2/GeoLite2-City.mmdb
  14. 3 0
      maxkey-authentications/maxkey-authentication-ip2location/src/main/resources/geoip2/LICENSE.txt
  15. 1 0
      maxkey-authentications/maxkey-authentication-ip2location/src/main/resources/geoip2/README.txt
  16. BIN
      maxkey-authentications/maxkey-authentication-ip2location/src/main/resources/ip2region/ip2region.xdb
  17. 66 0
      maxkey-authentications/maxkey-authentication-ip2location/src/test/java/org/dromara/maxkey/ip2location/Geoip2Test.java
  18. 40 0
      maxkey-authentications/maxkey-authentication-ip2location/src/test/java/org/dromara/maxkey/ip2location/Ip2RegionV2Test.java
  19. 32 0
      maxkey-authentications/maxkey-authentication-ip2location/src/test/java/org/dromara/maxkey/ip2location/IpRegionIp138Test.java
  20. 34 0
      maxkey-authentications/maxkey-authentication-ip2location/src/test/java/org/dromara/maxkey/ip2location/IpRegionParserTest.java
  21. 1 0
      settings.gradle

+ 12 - 0
maxkey-authentications/maxkey-authentication-ip2location/build.gradle

@@ -0,0 +1,12 @@
+
+description = "maxkey-authentication-core"
+
+
+dependencies {
+	//local jars
+	implementation fileTree(dir: '../maxkey-lib/', include: '*/*.jar')
+	
+	implementation project(":maxkey-common")
+	implementation project(":maxkey-core")
+   
+}

+ 141 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/autoconfigure/IpLocationAutoConfiguration.java

@@ -0,0 +1,141 @@
+/*
+ * Copyright [2023] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+
+package org.dromara.maxkey.autoconfigure;
+
+import org.dromara.maxkey.ip2location.IpLocation;
+import org.dromara.maxkey.ip2location.IpLocationParser;
+import org.dromara.maxkey.ip2location.offline.GeoIP2V4;
+import org.dromara.maxkey.ip2location.offline.Ip2regionV2;
+import org.dromara.maxkey.ip2location.online.Ip138;
+import org.lionsoul.ip2region.xdb.Searcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.util.StreamUtils;
+
+import com.maxmind.geoip2.DatabaseReader;
+
+/**
+ * 
+ * @author Crystal.Sea
+ *
+ */
+
+@AutoConfiguration
+public class IpLocationAutoConfiguration   implements InitializingBean {
+	static final  Logger _logger = LoggerFactory.getLogger(IpLocationAutoConfiguration.class);
+	
+	/**
+	 * 加载Ip2Region离线库数据 version 2.7.0
+	 * @return Ip2regionV2
+	 * @throws Exception
+	 */
+	public Ip2regionV2 ip2regionV2() throws Exception {
+		_logger.debug("IpRegion OffLine init...");
+		ClassPathResource resource = new ClassPathResource("/ip2region/ip2region.xdb");
+        byte[] dbBinStr = StreamUtils.copyToByteArray(resource.getInputStream());
+        _logger.debug("ip2region length {}",dbBinStr.length);
+        Searcher searcher = Searcher.newWithBuffer(dbBinStr);
+        return new Ip2regionV2(searcher);
+	}
+	
+	
+	/**
+	 * 加载GeoIP2离线库数据 version 4.0.1
+	 * @return GeoIp2V4
+	 * @throws Exception
+	 */
+	public GeoIP2V4 geoIP2() throws Exception {
+		_logger.debug("GeoIP2 OffLine init...");
+		ClassPathResource resource = new ClassPathResource("/geoip2/GeoLite2-City.mmdb");
+		DatabaseReader databaseReader = new DatabaseReader.Builder(resource.getInputStream()).build();
+        return new GeoIP2V4(databaseReader);
+	}
+	
+	/**
+	 * builder offline provider IpLocation
+	 * @param offlineProvider
+	 * @return IpLocation
+	 */
+	public IpLocation builderOfflineProvider(String offlineProvider) {
+		IpLocation ipLocationOffLine = null;
+		try {
+			if(offlineProvider.equalsIgnoreCase("none")) {
+				//do nothing
+				_logger.debug("IpLocation offline Provider none");
+			}else if(offlineProvider.equalsIgnoreCase("Ip2Region")){
+				ipLocationOffLine = ip2regionV2();
+				_logger.debug("IpLocation offline Provider Ip2Region");
+			}else if(offlineProvider.equalsIgnoreCase("GeoIp2")){
+				ipLocationOffLine = geoIP2();
+				_logger.debug("IpLocation offline Provider GeoIp2");
+			}
+		}catch(Exception e) {
+			_logger.error("builder Offline IpLocation error", e);
+		}
+		return ipLocationOffLine;
+	}
+	
+	/**
+	 * builder Online Provider IpLocation
+	 * @param onlineProvider
+	 * @return IpLocation
+	 */
+	public IpLocation builderOnlineProvider(String onlineProvider) {
+		//need on line provider
+		IpLocation ipLocationOnLine = null;
+		if(onlineProvider.equalsIgnoreCase("none")) {
+			//do nothing
+			_logger.debug("IpLocation online Provider none");
+		}else if(onlineProvider.equalsIgnoreCase("Ip138")){
+			ipLocationOnLine = new Ip138();
+			_logger.debug("IpLocation online Provider Ip138");
+		}
+		return ipLocationOnLine;
+	}
+	
+	/**
+	 * IP转换区域地址解析
+	 * @param isIplocation 是否转换
+	 * @param onlineProvider 在线转换实现提供商none/Ip138/Ipchaxun
+	 * @param offlineProvider 离线转换实现提供商none/Ip2Region/GeoIp2
+	 * @return IpLocationParser
+	 * @throws Exception
+	 */
+	@Bean 
+	public IpLocationParser ipLocationParser(
+			@Value("${maxkey.login.iplocation:false}") boolean isIplocation,
+			@Value("${maxkey.login.iplocation.online.provider:none}") String onlineProvider,
+			@Value("${maxkey.login.iplocation.offline.provider:none}") String offlineProvider) throws Exception {
+        return new IpLocationParser(
+        		isIplocation,
+				builderOnlineProvider(onlineProvider),
+				builderOfflineProvider(offlineProvider)
+		);
+	}
+	
+	@Override
+	public void afterPropertiesSet() throws Exception {
+
+	}
+
+}

+ 50 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/AbstractIpLocation.java

@@ -0,0 +1,50 @@
+package org.dromara.maxkey.ip2location;
+/*
+ * Copyright [2023] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+
+
+/**
+ * IpRegion转换抽象类,获取地址Location
+ * 
+ * @author Crystal.Sea
+ *
+ */
+public abstract class AbstractIpLocation implements IpLocation{
+
+	int failCount = 0;
+	
+	public int getFailCount() {
+		return failCount;
+	};
+	
+	public int plusFailCount() {
+		return failCount++;
+	};
+	
+	
+	public String getLocation(String region) {
+		if(region.endsWith("电信") || region.endsWith("移动") || region.endsWith("联通")) {
+			region.substring(0, region.length() - 2).trim();
+		}
+		
+		if(region.indexOf(" ") > 0) {
+			return region.split(" ")[0];
+		}
+		
+		return region;
+	}
+}

+ 39 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/IpLocation.java

@@ -0,0 +1,39 @@
+package org.dromara.maxkey.ip2location;
+/*
+ * Copyright [2023] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+
+
+/**
+ * IpRegion转换接口
+ * 
+ * @author Crystal.Sea
+ *
+ */
+public interface IpLocation {
+	public static final String USERAGENT = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36";
+	
+	public static final int  TIMEOUT 		= 5000;
+	
+	public Region region(String ipAddress);
+	
+	public String getLocation(String region);
+	
+	public int getFailCount();
+	
+	public int plusFailCount() ;
+	
+}

+ 35 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/IpLocationLocal.java

@@ -0,0 +1,35 @@
+package org.dromara.maxkey.ip2location;
+/*
+ * Copyright [2023] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+
+
+/**
+ * 本地(127.0.0.1/0:0:0:0:0:0:0:1)地址
+ * 
+ * @author Crystal.Sea
+ *
+ */
+public class IpLocationLocal extends AbstractIpLocation implements IpLocation{
+	
+	@Override
+	public Region region(String ipAddress) {
+		if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
+			return new Region("local");
+		}
+		return null;
+	}
+}

+ 73 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/IpLocationParser.java

@@ -0,0 +1,73 @@
+package org.dromara.maxkey.ip2location;
+/*
+ * Copyright [2023] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * IP转换区域地址解析
+ * 
+ * <p>
+ * 依次顺序为Local(本地) ->OnLine(在线解析) ->OffLine(离线解析) -> unknown(未知)
+ * </p>
+ * 
+ * @author Crystal.Sea
+ *
+ */
+public class IpLocationParser extends AbstractIpLocation  implements IpLocation{
+
+	IpLocation ipRegionLocal = new IpLocationLocal();
+	
+	IpLocation ipLocationOnLine;
+	
+	IpLocation ipLocationOffLine;
+	
+	boolean isIpLocation;
+
+	
+	public IpLocationParser() {
+	}
+
+
+	public IpLocationParser(boolean isIpLocation,IpLocation ipLocationOnLine, IpLocation ipLocationOffLine) {
+		super();
+		this.ipLocationOnLine  = ipLocationOnLine;
+		this.ipLocationOffLine = ipLocationOffLine;
+		this.isIpLocation = isIpLocation;
+	}
+
+	/**
+	 * ip转换区域地址
+	 */
+	@Override
+	public Region region(String ipAddress) {
+		Region region = null;
+		if( isIpLocation ){//true 需要转换,否则跳过
+			//本地转换
+			region = ipRegionLocal.region(ipAddress);
+			//在线转换
+			if(ipLocationOnLine != null && region == null) {
+				region = ipLocationOnLine.region(ipAddress);
+			}
+			//离线转换
+			if(ipLocationOffLine != null && region == null) {
+				region = ipLocationOffLine.region(ipAddress);
+			}
+		}
+		//不转换或者未找到返回unknown
+		return region == null ? new Region("unknown") : region;
+	}
+
+	
+}

+ 111 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/Region.java

@@ -0,0 +1,111 @@
+/*
+ * Copyright [2023] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+
+package org.dromara.maxkey.ip2location;
+
+/**
+ * IP所属区域实体
+ * 
+ * @author Crystal.sea
+ *
+ */
+public class Region {
+	
+	/**
+	 * 国家
+	 */
+	String country;
+	
+	/**
+	 * 省/州
+	 */
+	String province;
+	
+	/**
+	 * 城市
+	 */
+	String city;
+	
+	/**
+	 * 区域位置
+	 */
+	String addr;
+
+	public Region() {
+
+	}
+	
+	public Region(String addr) {
+		this.addr = addr;
+	}
+
+	public Region(String country, String province, String city, String addr) {
+		super();
+		this.country = country;
+		this.province = province;
+		this.city = city;
+		this.addr = addr;
+	}
+
+	public String getCountry() {
+		return country;
+	}
+
+	public void setCountry(String country) {
+		this.country = country;
+	}
+
+	public String getProvince() {
+		return province;
+	}
+
+	public void setProvince(String province) {
+		this.province = province;
+	}
+
+	public String getCity() {
+		return city;
+	}
+
+	public void setCity(String city) {
+		this.city = city;
+	}
+
+	public String getAddr() {
+		return addr;
+	}
+
+	public void setAddr(String addr) {
+		this.addr = addr;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append("Region [country=");
+		builder.append(country);
+		builder.append(", province=");
+		builder.append(province);
+		builder.append(", city=");
+		builder.append(city);
+		builder.append(", addr=");
+		builder.append(addr);
+		builder.append("]");
+		return builder.toString();
+	}
+	
+}

+ 57 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/offline/GeoIP2V4.java

@@ -0,0 +1,57 @@
+/*
+ * Copyright [2023] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.maxkey.ip2location.offline;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+import org.dromara.maxkey.ip2location.AbstractIpLocation;
+import org.dromara.maxkey.ip2location.IpLocation;
+import org.dromara.maxkey.ip2location.Region;
+
+import com.maxmind.geoip2.DatabaseReader;
+import com.maxmind.geoip2.exception.GeoIp2Exception;
+import com.maxmind.geoip2.model.CityResponse;
+
+public class GeoIP2V4  extends AbstractIpLocation implements IpLocation{
+	
+	DatabaseReader databaseReader;
+	
+	public GeoIP2V4(DatabaseReader databaseReader) {
+		this.databaseReader = databaseReader;
+	}
+
+	@Override
+	public Region region(String ipAddress) {
+        try {
+        	//解析IP地址
+			InetAddress inetAddress = InetAddress.getByName(ipAddress);
+			// 获取查询结果
+            CityResponse response = databaseReader.city(inetAddress);
+            // 获取国家信息
+            String country = response.getCountry().getNames().get("zh-CN");
+            // 获取省份/州
+            String state = response.getMostSpecificSubdivision().getNames().get("zh-CN");
+            // 获取城市
+            String city = response.getCity().getNames().get("zh-CN");
+            return new Region(country , state , city , country +" " + state + " " + city);
+		} catch (IOException | GeoIp2Exception e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+}

+ 61 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/offline/Ip2regionV2.java

@@ -0,0 +1,61 @@
+/*
+ * Copyright [2023] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.maxkey.ip2location.offline;
+
+import org.dromara.maxkey.ip2location.AbstractIpLocation;
+import org.dromara.maxkey.ip2location.IpLocation;
+import org.dromara.maxkey.ip2location.Region;
+import org.lionsoul.ip2region.xdb.Searcher;
+
+
+/**
+ * 基于ip2region离线库ip查询
+ * 
+ * <p>
+ * 官方文档:https://gitee.com/lionsoul/ip2region Apache-2.0
+ * </p>
+ * 
+ * <p>
+ * Ip2region (2.0 - xdb) 是一个离线 IP 数据管理框架和定位库,支持亿级别的数据段,10微秒级别的查询性能,提供了许多主流编程语言的 xdb 数据管理引擎的实现。
+ * </p>
+ * 
+ * @author Crystal.Sea
+ *
+ */
+public class Ip2regionV2 extends AbstractIpLocation implements IpLocation{
+	
+	Searcher searcher;;
+	
+	public Ip2regionV2(Searcher searcher) {
+		this.searcher = searcher;
+	}
+
+	@Override
+	public Region region(String ipAddress) {
+		try {
+			String regionAddr = searcher.search(ipAddress);
+			if(regionAddr.indexOf("内网IP")>-1) {
+				return new Region("内网IP");
+			}
+			String[] regionAddrs =regionAddr.split("\\|");
+			return new Region(regionAddrs[0],regionAddrs[2],regionAddrs[3],regionAddrs[0]+regionAddrs[2]+regionAddrs[3]);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+	
+}

+ 61 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/online/Ip138.java

@@ -0,0 +1,61 @@
+/*
+ * Copyright [2023] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.maxkey.ip2location.online;
+
+import java.io.IOException;
+
+import org.dromara.maxkey.ip2location.AbstractIpLocation;
+import org.dromara.maxkey.ip2location.IpLocation;
+import org.dromara.maxkey.ip2location.Region;
+import org.dromara.maxkey.util.JsonUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+
+/**
+ * Ip138查询ip地址
+ * 
+ * @author Crystal.Sea
+ *
+ */
+public class Ip138 extends AbstractIpLocation implements IpLocation{
+	
+	public static final String REGION_URL = "https://www.ip138.com/iplookup.asp?ip=%s&action=2";
+	
+	public static final String BEGIN 	= "\"ip_c_list\":[";
+	public static final String END 		= "], \"zg\":1};";
+	
+	@Override
+	public Region region(String ipAddress) {
+		try {
+			Document doc;
+			doc = Jsoup.connect(String.format(REGION_URL, ipAddress))
+					.timeout(TIMEOUT)
+					.userAgent(USERAGENT)
+					.header("Host", "www.ip138.com")
+					.header("Referer", "https://www.ip138.com/")
+					.header("sec-ch-ua", "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"98\", \"Google Chrome\";v=\"98\"")
+					.header("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9")
+					.get();
+			String htmlData = doc.toString();
+			String jsonData = htmlData.substring(htmlData.indexOf(BEGIN) + BEGIN.length() , htmlData.indexOf(END));
+			Ip138Response response = JsonUtils.stringToObject(jsonData, Ip138Response.class);
+			return response == null ? null : new Region(response.getCt(),response.getProv(),response.getCity(),getLocation(response.toString()));
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+}

+ 81 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/main/java/org/dromara/maxkey/ip2location/online/Ip138Response.java

@@ -0,0 +1,81 @@
+/*
+ * Copyright [2023] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.maxkey.ip2location.online;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+/**
+ * Ip138返回结果
+ * 
+ * @author Crystal.Sea
+ *
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Ip138Response {
+
+	String ct;
+	String prov;
+	String city;
+	String area;
+	String yunyin;
+	
+	public Ip138Response() {
+	}
+	public String getCt() {
+		return ct;
+	}
+	public void setCt(String ct) {
+		this.ct = ct;
+	}
+	public String getProv() {
+		return prov;
+	}
+	public void setProv(String prov) {
+		this.prov = prov;
+	}
+	public String getCity() {
+		return city;
+	}
+	public void setCity(String city) {
+		this.city = city;
+	}
+	public String getArea() {
+		return area;
+	}
+	public void setArea(String area) {
+		this.area = area;
+	}
+	public String getYunyin() {
+		return yunyin;
+	}
+	public void setYunyin(String yunyin) {
+		this.yunyin = yunyin;
+	}
+	
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append(ct)
+			   .append(prov)
+			   .append(city)
+			   .append(area)
+			   .append(" ")
+			   .append(yunyin);
+		return builder.toString();
+	}
+	
+	
+}

+ 1 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/main/resources/geoip2/COPYRIGHT.txt

@@ -0,0 +1 @@
+Database and Contents Copyright (c) 2022 MaxMind, Inc.

BIN
maxkey-authentications/maxkey-authentication-ip2location/src/main/resources/geoip2/GeoLite2-City.mmdb


+ 3 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/main/resources/geoip2/LICENSE.txt

@@ -0,0 +1,3 @@
+Use of this MaxMind product is governed by MaxMind's GeoLite2 End User License Agreement, which can be viewed at https://www.maxmind.com/en/geolite2/eula.
+
+This database incorporates GeoNames [https://www.geonames.org] geographical data, which is made available under the Creative Commons Attribution 4.0 License. To view a copy of this license, visit https://creativecommons.org/licenses/by/4.0/.

+ 1 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/main/resources/geoip2/README.txt

@@ -0,0 +1 @@
+Latitude and longitude are not precise and should not be used to identify a particular street address or household.

BIN
maxkey-authentications/maxkey-authentication-ip2location/src/main/resources/ip2region/ip2region.xdb


+ 66 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/test/java/org/dromara/maxkey/ip2location/Geoip2Test.java

@@ -0,0 +1,66 @@
+/*
+ * Copyright [2023] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.maxkey.ip2location;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+import org.springframework.core.io.ClassPathResource;
+
+import com.maxmind.geoip2.DatabaseReader;
+import com.maxmind.geoip2.exception.GeoIp2Exception;
+import com.maxmind.geoip2.model.CityResponse;
+
+public class Geoip2Test {
+
+	public static void main(String[] args) throws IOException {
+		// TODO Auto-generated method stub
+		ClassPathResource resource = new ClassPathResource("/geoip2/GeoLite2-City.mmdb");
+		String ip = "117.155.70.59";
+	
+        if (!resource.getFile().exists()) {
+            System.out.println("Error: Invalid GeoLite2-City.mmdb file, filePath:" + resource.getFile().getPath());
+            
+        }
+        // 读取数据库内容
+        DatabaseReader reader = null;
+        try {
+            reader = new DatabaseReader.Builder(resource.getFile()).build();
+            //解析IP地址
+            InetAddress ipAddress = InetAddress.getByName(ip);
+            // 获取查询结果
+            CityResponse response = reader.city(ipAddress);
+            // 获取国家信息
+            String country = response.getCountry().getNames().get("zh-CN");
+            // 获取省份
+            String state = response.getMostSpecificSubdivision().getNames().get("zh-CN");
+            //查询不到时保持与ip2region方式的返回结果一致
+            if (state == null){
+            	state = "0";
+            }
+            // 获取城市
+            String city = response.getCity().getNames().get("zh-CN");
+            if (city == null){
+            	city = "0";
+            }
+            String[] resu = {state,city};
+            System.out.println(" " +country+" " +state +" " +city);
+        } catch (IOException | GeoIp2Exception e) {
+            e.printStackTrace();
+        }
+	}
+
+}

+ 40 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/test/java/org/dromara/maxkey/ip2location/Ip2RegionV2Test.java

@@ -0,0 +1,40 @@
+/*
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+
+package org.dromara.maxkey.ip2location;
+
+import org.dromara.maxkey.ip2location.offline.Ip2regionV2;
+import org.lionsoul.ip2region.xdb.Searcher;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.util.StreamUtils;
+
+public class Ip2RegionV2Test {
+
+	public static void main(String[] args) throws Exception {
+		// TODO Auto-generated method stub
+		String ip ="101.227.131.220";
+		ClassPathResource resource = new ClassPathResource("/ip2region/ip2region.xdb");
+        byte[] dbBinStr = StreamUtils.copyToByteArray(resource.getInputStream());
+        System.out.println(dbBinStr.length);
+        //_logger.debug("ip2region length {}",dbBinStr.length);
+        Searcher searcher = Searcher.newWithBuffer(dbBinStr);
+        Ip2regionV2 ipRegionV2OffLine = new Ip2regionV2(searcher);
+        String region = ipRegionV2OffLine.region(ip).toString();
+        System.out.println(region);
+
+	}
+}

+ 32 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/test/java/org/dromara/maxkey/ip2location/IpRegionIp138Test.java

@@ -0,0 +1,32 @@
+/*
+ * Copyright [2023] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.dromara.maxkey.ip2location;
+
+ 
+import org.dromara.maxkey.ip2location.IpLocation;
+import org.dromara.maxkey.ip2location.online.Ip138;
+import org.junit.Test;
+
+public class IpRegionIp138Test {
+	
+	@Test
+	public void test(){
+		IpLocation ipRegion = new Ip138();
+		System.out.println(ipRegion.region("117.155.70.59"));
+	}
+	
+}

+ 34 - 0
maxkey-authentications/maxkey-authentication-ip2location/src/test/java/org/dromara/maxkey/ip2location/IpRegionParserTest.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright [2023] [MaxKey of copyright http://www.maxkey.top]
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.maxkey.ip2location;
+
+import org.dromara.maxkey.ip2location.IpLocationParser;
+import org.dromara.maxkey.ip2location.online.Ip138;
+import org.junit.Test;
+
+public class IpRegionParserTest {
+
+	@Test
+	public void test(){
+		System.out.println(
+				new IpLocationParser().region("127.0.0.1")
+				);
+		
+		System.out.println(
+				new IpLocationParser(true,new Ip138(),null).region("117.155.70.59")
+				);
+	}
+}

+ 1 - 0
settings.gradle

@@ -28,6 +28,7 @@ include (
 	//authentications
 	'maxkey-authentications:maxkey-authentication-core',
 	'maxkey-authentications:maxkey-authentication-captcha',
+	'maxkey-authentications:maxkey-authentication-ip2location',
 	'maxkey-authentications:maxkey-authentication-social',
 	'maxkey-authentications:maxkey-authentication-otp',
 	'maxkey-authentications:maxkey-authentication-provider',