Răsfoiți Sursa

IpRegion IP地址转换为行政区域

Ip138 ,Ip360 ,Ipchaxun ,Ipcn ,Pconline & Local
MaxKey 3 ani în urmă
părinte
comite
13102d53b7
32 a modificat fișierele cu 604 adăugiri și 73 ștergeri
  1. 18 14
      ReleaseNotes.txt
  2. 2 1
      build.gradle
  3. 4 2
      config/build_docker.gradle
  4. 4 2
      config/build_jar.gradle
  5. 4 2
      config/build_standard.gradle
  6. 2 0
      gradle.properties
  7. 4 0
      maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/RealmAuthenticationProvider.java
  8. 4 3
      maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/realm/AbstractAuthenticationRealm.java
  9. 71 43
      maxkey-core/src/main/java/org/maxkey/entity/HistoryLogin.java
  10. 18 0
      maxkey-core/src/main/java/org/maxkey/entity/UserInfo.java
  11. 6 5
      maxkey-core/src/main/java/org/maxkey/persistence/repository/LoginHistoryRepository.java
  12. 2 0
      maxkey-core/src/main/java/org/maxkey/persistence/repository/LoginRepository.java
  13. 1 1
      maxkey-core/src/main/java/org/maxkey/web/WebInstRequestFilter.java
  14. 18 0
      maxkey-core/src/main/java/org/maxkey/web/ipregion/AbstractIpRegion.java
  15. 16 0
      maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegion.java
  16. 57 0
      maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionFactory.java
  17. 37 0
      maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionIp138.java
  18. 60 0
      maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionIp138Response.java
  19. 38 0
      maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionIp360.java
  20. 40 0
      maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionIp360Response.java
  21. 34 0
      maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionIpchaxun.java
  22. 31 0
      maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionIpcn.java
  23. 12 0
      maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionLocal.java
  24. 18 0
      maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionPconline.java
  25. 20 0
      maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionPconlineResponse.java
  26. 1 0
      maxkey-core/src/main/java/org/maxkey/web/ipregion/package-info.java
  27. 17 0
      maxkey-core/src/test/java/org/maxkey/web/ipregion/IpRegionFactoryTest.java
  28. 13 0
      maxkey-core/src/test/java/org/maxkey/web/ipregion/IpRegionIp138Test.java
  29. 13 0
      maxkey-core/src/test/java/org/maxkey/web/ipregion/IpRegionIp360Test.java
  30. 13 0
      maxkey-core/src/test/java/org/maxkey/web/ipregion/IpRegionIpchaxunTest.java
  31. 13 0
      maxkey-core/src/test/java/org/maxkey/web/ipregion/IpRegionIpcnTest.java
  32. 13 0
      maxkey-core/src/test/java/org/maxkey/web/ipregion/IpRegionPconlineTest.java

+ 18 - 14
ReleaseNotes.txt

@@ -3,20 +3,23 @@
     *(MAXKEY-220402) Active Directory域控组织、账号同步优化
     *(MAXKEY-220403) OAuth及OIDC兼容 Authorization/authorization的bearer #I4VFYD(无双的英雄peerless_hero)
     *(MAXKEY-220404) 优化实时同步,增加RocketMQ支持
-    *(MAXKEY-220405) 官方网站内容优化和完善
-    *(MAXKEY-220406) 图片验证码可配置调整;支持数字和算术计算;长度80,高度40
-    *(MAXKEY-220407) RSAUtils优化增加签名和验证功能,优化PEM生成器功能
-    *(MAXKEY-220408) 管理端登录验证码高度不一致调整
-    *(MAXKEY-220409) docker配置文件中DATABASE_HOST改为localhost
-    *(MAXKEY-220410) 注册默认租户ID设置为1
-    *(MAXKEY-220411) 增加LDAP账号映射功能,登录账号和LDAP账号可进行映射
-    *(MAXKEY-220412) 日志功能优化
-    *(MAXKEY-220413) 配置管理默认登录后地址
-    *(MAXKEY-220414) 服务端删除对maxkey-client-sdk依赖
-    *(MAXKEY-220415) 产品简短说明,添加badges标志,产品功能说明优化
-    *(MAXKEY-220416) 增加用户、机构等表默认值
-    *(MAXKEY-220417) Metadatas功能优化
-    *(MAXKEY-220418) 依赖项引用、更新和升级
+    *(MAXKEY-220405) IP地址转换为行政区域,后续增加异地登录风险通知
+    *(MAXKEY-220406) 官方网站内容优化和完善
+    *(MAXKEY-220407) 图片验证码可配置调整;支持数字和算术计算;长度80,高度40
+    *(MAXKEY-220408) RSAUtils优化增加签名和验证功能,优化PEM生成器功能
+    *(MAXKEY-220409) 管理端登录验证码高度不一致调整
+    *(MAXKEY-220410) docker配置文件中DATABASE_HOST改为localhost
+    *(MAXKEY-220411) 注册默认租户ID设置为1
+    *(MAXKEY-220412) 增加LDAP账号映射功能,登录账号和LDAP账号可进行映射
+    *(MAXKEY-220413) 日志功能优化
+    *(MAXKEY-220414) 配置管理默认登录后地址
+    *(MAXKEY-220415) 服务端删除对maxkey-client-sdk依赖
+    *(MAXKEY-220416) 产品简短说明,添加badges标志,产品功能说明优化
+    *(MAXKEY-220417) 增加用户、机构等表默认值
+    *(MAXKEY-220418) Metadatas功能优化
+    *(MAXKEY-220419) 头像保存出错BUG修复 #I4VQDD(无双的英雄peerless_hero)
+    *(MAXKEY-220420) 用户资料空值问题修复 #I4VNPO(无双的英雄peerless_hero)
+    *(MAXKEY-220421) 依赖项引用、更新和升级
         spring                   5.3.16
         springBoot               2.6.4
         springSecurity           5.6.2
@@ -26,6 +29,7 @@
         commonsvalidator         1.7
         RocketMQclient           4.9.2
         RocketmMQspringboot      2.2.1
+        jsoup                    1.14.3
         
 MaxKey v 3.3.2 GA 2022/02/17
     *(MAXKEY-220301) 统一界面色调

+ 2 - 1
build.gradle

@@ -397,10 +397,11 @@ subprojects {
          implementation group: 'io.micrometer', name: 'micrometer-registry-prometheus', version: "${micrometercoreVersion}"
          implementation group: 'org.latencyutils', name: 'LatencyUtils', version: "${LatencyUtilsVersion}"
          implementation group: 'org.codehaus.woodstox', name: 'stax2-api', version: "${stax2apiVersion}"
-         implementation group: 'org.reflections', name: 'reflections', version: '0.9.11'
+         implementation group: 'org.reflections', name: 'reflections', version: "${reflectionsVersion}"
          implementation group: 'io.prometheus', name: 'simpleclient', version: "${prometheusVersion}"
          implementation group: 'io.prometheus', name: 'simpleclient_common', version: "${prometheusVersion}"
          implementation group: 'com.belerweb', name: 'pinyin4j', version: "${pinyin4jVersion}"
+         implementation group: 'org.jsoup', name: 'jsoup', version: "${jsoupVersion}"
          
          implementation group: 'io.netty', name: 'netty-all', version: "${nettyVersion}"
           //阿里云

+ 4 - 2
config/build_docker.gradle

@@ -361,9 +361,11 @@ subprojects {
          implementation group: 'io.micrometer', name: 'micrometer-core', version: "${micrometercoreVersion}"
          implementation group: 'org.latencyutils', name: 'LatencyUtils', version: "${LatencyUtilsVersion}"
          implementation group: 'org.codehaus.woodstox', name: 'stax2-api', version: "${stax2apiVersion}"
-         implementation group: 'org.reflections', name: 'reflections', version: '0.9.11'
-         implementation group: 'io.prometheus', name: 'simpleclient', version: '0.5.0'
+         implementation group: 'org.reflections', name: 'reflections', version: "${reflectionsVersion}"
+         implementation group: 'io.prometheus', name: 'simpleclient', version: "${prometheusVersion}"
+         implementation group: 'io.prometheus', name: 'simpleclient_common', version: "${prometheusVersion}"
          implementation group: 'com.belerweb', name: 'pinyin4j', version: "${pinyin4jVersion}"
+         implementation group: 'org.jsoup', name: 'jsoup', version: "${jsoupVersion}"
          
          implementation group: 'io.netty', name: 'netty-all', version: "${nettyVersion}"
           //阿里云

+ 4 - 2
config/build_jar.gradle

@@ -361,9 +361,11 @@ subprojects {
          implementation group: 'io.micrometer', name: 'micrometer-core', version: "${micrometercoreVersion}"
          implementation group: 'org.latencyutils', name: 'LatencyUtils', version: "${LatencyUtilsVersion}"
          implementation group: 'org.codehaus.woodstox', name: 'stax2-api', version: "${stax2apiVersion}"
-         implementation group: 'org.reflections', name: 'reflections', version: '0.9.11'
-         implementation group: 'io.prometheus', name: 'simpleclient', version: '0.5.0'
+         implementation group: 'org.reflections', name: 'reflections', version: "${reflectionsVersion}"
+         implementation group: 'io.prometheus', name: 'simpleclient', version: "${prometheusVersion}"
+         implementation group: 'io.prometheus', name: 'simpleclient_common', version: "${prometheusVersion}"
          implementation group: 'com.belerweb', name: 'pinyin4j', version: "${pinyin4jVersion}"
+         implementation group: 'org.jsoup', name: 'jsoup', version: "${jsoupVersion}"
          
          implementation group: 'io.netty', name: 'netty-all', version: "${nettyVersion}"
           //阿里云

+ 4 - 2
config/build_standard.gradle

@@ -396,9 +396,11 @@ subprojects {
          implementation group: 'io.micrometer', name: 'micrometer-core', version: "${micrometercoreVersion}"
          implementation group: 'org.latencyutils', name: 'LatencyUtils', version: "${LatencyUtilsVersion}"
          implementation group: 'org.codehaus.woodstox', name: 'stax2-api', version: "${stax2apiVersion}"
-         implementation group: 'org.reflections', name: 'reflections', version: '0.9.11'
-         implementation group: 'io.prometheus', name: 'simpleclient', version: '0.5.0'
+         implementation group: 'org.reflections', name: 'reflections', version: "${reflectionsVersion}"
+         implementation group: 'io.prometheus', name: 'simpleclient', version: "${prometheusVersion}"
+         implementation group: 'io.prometheus', name: 'simpleclient_common', version: "${prometheusVersion}"
          implementation group: 'com.belerweb', name: 'pinyin4j', version: "${pinyin4jVersion}"
+         implementation group: 'org.jsoup', name: 'jsoup', version: "${jsoupVersion}"
          
          implementation group: 'io.netty', name: 'netty-all', version: "${nettyVersion}"
           //阿里云

+ 2 - 0
gradle.properties

@@ -162,6 +162,8 @@ aspectjtoolsVersion             =1.9.4
 evictorVersion                  =1.0.0
 lettuceVersion                  =6.1.4.RELEASE
 pinyin4jVersion                 =2.5.1
+jsoupVersion                    =1.14.3
+reflectionsVersion              =0.9.11
 #xml
 jdomVersion                     =2.0.2
 dom4jVersion                    =1.6.1

+ 4 - 0
maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/RealmAuthenticationProvider.java

@@ -228,6 +228,10 @@ public class RealmAuthenticationProvider extends AbstractAuthenticationProvider
         WebContext.setAuthentication(authenticationToken);
         
         WebContext.setAttribute(WebConstants.CURRENT_USER_SESSION_ID, currentUserSessionId);
+        
+        if(!WebContext.getInst(WebContext.getRequest()).equalsIgnoreCase(userInfo.getInstId())){
+        	//TODO :
+        }
         return authenticationToken;
     }
   

+ 4 - 3
maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/realm/AbstractAuthenticationRealm.java

@@ -34,6 +34,7 @@ import org.maxkey.persistence.service.UserInfoService;
 import org.maxkey.util.DateUtils;
 import org.maxkey.web.WebConstants;
 import org.maxkey.web.WebContext;
+import org.maxkey.web.ipregion.IpRegionFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.jdbc.core.JdbcTemplate;
@@ -49,8 +50,6 @@ public abstract class AbstractAuthenticationRealm {
 
     protected JdbcTemplate jdbcTemplate;
     
-    protected boolean provisioning;
-    
     protected PasswordPolicyValidator passwordPolicyValidator;
     
     protected LoginRepository loginRepository;
@@ -136,7 +135,7 @@ public abstract class AbstractAuthenticationRealm {
         HistoryLogin historyLogin = new HistoryLogin();
         historyLogin.setSessionId(WebContext.genId());
         historyLogin.setSessionStatus(7);
-        if(WebContext.getAttribute(WebConstants.CURRENT_USER_SESSION_ID) !=null) {
+        if(WebContext.getAttribute(WebConstants.CURRENT_USER_SESSION_ID) != null) {
             historyLogin.setSessionStatus(1);
             historyLogin.setSessionId(WebContext.getAttribute(WebConstants.CURRENT_USER_SESSION_ID).toString());
         }
@@ -150,6 +149,8 @@ public abstract class AbstractAuthenticationRealm {
         historyLogin.setBrowser(browser.getName());
         historyLogin.setPlatform(browser.getPlatform());
         historyLogin.setSourceIp(userInfo.getLastLoginIp());
+        historyLogin.setIpRegion(IpRegionFactory.getFactory().region(userInfo.getLastLoginIp()));
+        historyLogin.setIpLocation(IpRegionFactory.getFactory().getLocation(historyLogin.getIpRegion()));
         historyLogin.setProvider(provider);
         historyLogin.setCode(code);
         historyLogin.setLoginType(type);

+ 71 - 43
maxkey-core/src/main/java/org/maxkey/entity/HistoryLogin.java

@@ -64,6 +64,10 @@ public class HistoryLogin  extends JpaBaseEntity  implements Serializable{
 	@Column
 	String sourceIp;
 	@Column
+	String ipRegion;
+	@Column
+	String ipLocation;
+	@Column
 	String browser;
 	@Column
 	String platform;
@@ -169,6 +173,22 @@ public class HistoryLogin  extends JpaBaseEntity  implements Serializable{
 		this.sourceIp = sourceIp;
 	}
 
+	public String getIpRegion() {
+		return ipRegion;
+	}
+
+	public void setIpRegion(String ipRegion) {
+		this.ipRegion = ipRegion;
+	}
+
+	public String getIpLocation() {
+		return ipLocation;
+	}
+
+	public void setIpLocation(String ipLocation) {
+		this.ipLocation = ipLocation;
+	}
+
 	public String getBrowser() {
 		return browser;
 	}
@@ -258,47 +278,55 @@ public class HistoryLogin  extends JpaBaseEntity  implements Serializable{
 	}
 
 	@Override
-    public String toString() {
-        StringBuilder builder = new StringBuilder();
-        builder.append("HistoryLogin [id=");
-        builder.append(id);
-        builder.append(", sessionId=");
-        builder.append(sessionId);
-        builder.append(", userId=");
-        builder.append(userId);
-        builder.append(", username=");
-        builder.append(username);
-        builder.append(", displayName=");
-        builder.append(displayName);
-        builder.append(", loginType=");
-        builder.append(loginType);
-        builder.append(", message=");
-        builder.append(message);
-        builder.append(", code=");
-        builder.append(code);
-        builder.append(", provider=");
-        builder.append(provider);
-        builder.append(", sourceIp=");
-        builder.append(sourceIp);
-        builder.append(", browser=");
-        builder.append(browser);
-        builder.append(", platform=");
-        builder.append(platform);
-        builder.append(", application=");
-        builder.append(application);
-        builder.append(", loginUrl=");
-        builder.append(loginUrl);
-        builder.append(", loginTime=");
-        builder.append(loginTime);
-        builder.append(", logoutTime=");
-        builder.append(logoutTime);
-        builder.append(", startDate=");
-        builder.append(startDate);
-        builder.append(", endDate=");
-        builder.append(endDate);
-        builder.append("]");
-        return builder.toString();
-    }
-
-
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append("HistoryLogin [id=");
+		builder.append(id);
+		builder.append(", sessionId=");
+		builder.append(sessionId);
+		builder.append(", userId=");
+		builder.append(userId);
+		builder.append(", username=");
+		builder.append(username);
+		builder.append(", displayName=");
+		builder.append(displayName);
+		builder.append(", loginType=");
+		builder.append(loginType);
+		builder.append(", message=");
+		builder.append(message);
+		builder.append(", code=");
+		builder.append(code);
+		builder.append(", provider=");
+		builder.append(provider);
+		builder.append(", sourceIp=");
+		builder.append(sourceIp);
+		builder.append(", ipRegion=");
+		builder.append(ipRegion);
+		builder.append(", ipLocation=");
+		builder.append(ipLocation);
+		builder.append(", browser=");
+		builder.append(browser);
+		builder.append(", platform=");
+		builder.append(platform);
+		builder.append(", application=");
+		builder.append(application);
+		builder.append(", loginUrl=");
+		builder.append(loginUrl);
+		builder.append(", loginTime=");
+		builder.append(loginTime);
+		builder.append(", logoutTime=");
+		builder.append(logoutTime);
+		builder.append(", instId=");
+		builder.append(instId);
+		builder.append(", instName=");
+		builder.append(instName);
+		builder.append(", sessionStatus=");
+		builder.append(sessionStatus);
+		builder.append(", startDate=");
+		builder.append(startDate);
+		builder.append(", endDate=");
+		builder.append(endDate);
+		builder.append("]");
+		return builder.toString();
+	}
 }

+ 18 - 0
maxkey-core/src/main/java/org/maxkey/entity/UserInfo.java

@@ -145,6 +145,8 @@ public class UserInfo extends JpaBaseEntity {
     protected String lastLogoffTime;
     protected int passwordSetType;
     protected Integer loginCount;
+    protected String regionHistory;
+    protected String passwordHistory;
 
     @Column
     protected String locale;
@@ -1280,6 +1282,22 @@ public class UserInfo extends JpaBaseEntity {
 		this.instName = instName;
 	}
 
+	public String getRegionHistory() {
+		return regionHistory;
+	}
+
+	public void setRegionHistory(String regionHistory) {
+		this.regionHistory = regionHistory;
+	}
+
+	public String getPasswordHistory() {
+		return passwordHistory;
+	}
+
+	public void setPasswordHistory(String passwordHistory) {
+		this.passwordHistory = passwordHistory;
+	}
+
 	@Override
 	public String toString() {
 		StringBuilder builder = new StringBuilder();

+ 6 - 5
maxkey-core/src/main/java/org/maxkey/persistence/repository/LoginHistoryRepository.java

@@ -28,7 +28,7 @@ import org.springframework.jdbc.core.JdbcTemplate;
 public class LoginHistoryRepository {
     private static Logger _logger = LoggerFactory.getLogger(LoginHistoryRepository.class);
     
-    private static final String HISTORY_LOGIN_INSERT_STATEMENT = "insert into mxk_history_login (id , sessionid , userid , username , displayname , logintype , message , code , provider , sourceip , browser , platform , application , loginurl , sessionstatus ,instid)values( ? , ? , ? , ? , ? , ? , ?, ? , ? , ?, ? , ? , ?, ? , ? , ?)";
+    private static final String HISTORY_LOGIN_INSERT_STATEMENT = "insert into mxk_history_login (id , sessionid , userid , username , displayname , logintype , message , code , provider , sourceip , ipregion , iplocation, browser , platform , application , loginurl , sessionstatus ,instid)values( ? , ? , ? , ? , ? , ? , ? , ? , ?, ? , ? , ?, ? , ? , ?, ? , ? , ?)";
 
     private static final String HISTORY_LOGOUT_UPDATE_STATEMENT = "update mxk_history_login set logouttime = ? ,sessionstatus = 7 where  sessionid = ?";
 
@@ -46,14 +46,15 @@ public class LoginHistoryRepository {
                 new Object[] { 
                         historyLogin.getId(), historyLogin.getSessionId(), historyLogin.getUserId(), historyLogin.getUsername(),
                         historyLogin.getDisplayName(), historyLogin.getLoginType(), historyLogin.getMessage(), historyLogin.getCode(), 
-                        historyLogin.getProvider(), historyLogin.getSourceIp(), historyLogin.getBrowser(), historyLogin.getPlatform(),
-                        "Browser", historyLogin.getLoginUrl() , historyLogin.getSessionStatus(),historyLogin.getInstId()
+                        historyLogin.getProvider(), historyLogin.getSourceIp(),historyLogin.getIpRegion(),historyLogin.getIpLocation(),
+                        historyLogin.getBrowser(), historyLogin.getPlatform(),"Browser", historyLogin.getLoginUrl() , 
+                        historyLogin.getSessionStatus(),historyLogin.getInstId()
                         },
                 new int[] { 
                         Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, 
                         Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
-                        Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR ,Types.INTEGER,
-                        Types.VARCHAR
+                        Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, 
+                        Types.VARCHAR ,Types.INTEGER, Types.VARCHAR
                         });
     }
     

+ 2 - 0
maxkey-core/src/main/java/org/maxkey/persistence/repository/LoginRepository.java

@@ -321,6 +321,8 @@ public class LoginRepository {
             userInfo.setLastLoginIp(rs.getString("lastloginip"));
             userInfo.setLastLogoffTime(rs.getString("lastlogofftime"));
             userInfo.setLoginCount(rs.getInt("logincount"));
+            userInfo.setRegionHistory(rs.getString("regionhistory"));
+            userInfo.setPasswordHistory(rs.getString("passwordhistory"));
 
             userInfo.setTimeZone(rs.getString("timezone"));
             userInfo.setLocale(rs.getString("locale"));

+ 1 - 1
maxkey-core/src/main/java/org/maxkey/web/WebInstRequestFilter.java

@@ -58,7 +58,7 @@ public class WebInstRequestFilter  extends GenericFilterBean {
 			if(host.indexOf(":")> -1 ) {
 				host = host.split(":")[0];
 			}
-			Institutions institution =institutionsRepository.findByDomain(host);
+			Institutions institution = institutionsRepository.findByDomain(host);
 			_logger.trace("{}" ,institution);
 			request.getSession().setAttribute(WebConstants.CURRENT_INST, institution);
 			WebContext.setCookie((HttpServletResponse)servletResponse, host, WebConstants.INST_COOKIE_NAME, institution.getId());

+ 18 - 0
maxkey-core/src/main/java/org/maxkey/web/ipregion/AbstractIpRegion.java

@@ -0,0 +1,18 @@
+package org.maxkey.web.ipregion;
+
+public abstract class AbstractIpRegion implements IpRegion{
+
+	int failCount = 0;
+	
+	public String getLocation(String region) {
+		return region;
+	}
+	
+	public int getFailCount() {
+		return failCount;
+	};
+	
+	public int plusFailCount() {
+		return failCount++;
+	};
+}

+ 16 - 0
maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegion.java

@@ -0,0 +1,16 @@
+package org.maxkey.web.ipregion;
+
+public interface IpRegion {
+	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 String region(String ipAddress);
+	
+	public String getLocation(String region);
+	
+	public int getFailCount();
+	
+	public int plusFailCount() ;
+	
+}

+ 57 - 0
maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionFactory.java

@@ -0,0 +1,57 @@
+package org.maxkey.web.ipregion;
+
+import java.util.ArrayList;
+
+import org.apache.commons.lang3.StringUtils;
+
+public class IpRegionFactory extends AbstractIpRegion  implements IpRegion{
+
+	static IpRegionFactory factory = new IpRegionFactory();
+	
+	static final ArrayList<IpRegion> ipRegionList;
+	
+	static {
+		ipRegionList = new ArrayList<IpRegion>();
+		ipRegionList.add(new IpRegionLocal());
+		ipRegionList.add(new IpRegionIp138());
+		ipRegionList.add(new IpRegionIpchaxun());
+		ipRegionList.add(new IpRegionIpcn());
+		ipRegionList.add(new IpRegionIp360());
+		ipRegionList.add(new IpRegionPconline());
+	}
+	
+	public static IpRegion getFactory() {
+		return factory;
+	}
+	
+	@Override
+	public String region(String ipAddress) {
+		for(int i = 0 ; i<ipRegionList.size() ; i++ ) {
+			IpRegion ipRegion = ipRegionList.get(i);
+			String region = ipRegion.region(ipAddress);
+			if(StringUtils.isNotBlank(region)) {
+				return region;
+			}else {
+				if(ipRegion.getFailCount() > 6) {
+					ipRegionList.remove(i);//remove from list
+				}
+				//fail plus 1
+				ipRegion.plusFailCount();
+			}
+		}
+		return "unknown";
+	}
+	
+	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;
+	}
+	
+}

+ 37 - 0
maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionIp138.java

@@ -0,0 +1,37 @@
+package org.maxkey.web.ipregion;
+
+import java.io.IOException;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.maxkey.util.JsonUtils;
+
+public class IpRegionIp138 extends AbstractIpRegion implements IpRegion{
+	
+	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 String 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));
+			IpRegionIp138Response responseJson = JsonUtils.json2Object(jsonData, IpRegionIp138Response.class);
+			return responseJson == null ? null : responseJson.toString();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+}

+ 60 - 0
maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionIp138Response.java

@@ -0,0 +1,60 @@
+package org.maxkey.web.ipregion;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class IpRegionIp138Response {
+
+	String ct;
+	String prov;
+	String city;
+	String area;
+	String yunyin;
+	
+	public IpRegionIp138Response() {
+	}
+	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();
+	}
+	
+	
+}

+ 38 - 0
maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionIp360.java

@@ -0,0 +1,38 @@
+package org.maxkey.web.ipregion;
+
+import java.io.IOException;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.maxkey.util.JsonUtils;
+
+public class IpRegionIp360  extends AbstractIpRegion  implements IpRegion{
+	
+	public static final String REGION_URL = "http://ip.360.cn/IPQuery/ipquery?ip=%s&verifycode=";
+	public static final String BEGIN 	= "<body>";
+	public static final String END 		= "</body>";
+	
+	@Override
+	public String region(String ipAddress) {
+		try {
+			Document doc;
+			doc = Jsoup.connect(String.format(REGION_URL, ipAddress))
+					.timeout(TIMEOUT)
+					.userAgent(USERAGENT)
+					.header("Host", "ip.360.cn")
+					.header("Origin", "http://ip.360.cn")
+					.header("Referer", "http://ip.360.cn/")
+					.header("Accept","application/json, text/plain, */*")
+					.post();
+			
+			String htmlData = doc.toString();
+			String jsonData = htmlData.substring(htmlData.indexOf(BEGIN) + BEGIN.length() , htmlData.indexOf(END));
+			IpRegionIp360Response responseJson = JsonUtils.json2Object(jsonData, IpRegionIp360Response.class);
+			return responseJson == null ? null : responseJson.getData().replace("\t", " ");
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+	
+}

+ 40 - 0
maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionIp360Response.java

@@ -0,0 +1,40 @@
+package org.maxkey.web.ipregion;
+
+public class IpRegionIp360Response {
+
+	int errno;
+	String errmsg;
+	String data;
+	public int getErrno() {
+		return errno;
+	}
+	public void setErrno(int errno) {
+		this.errno = errno;
+	}
+	public String getErrmsg() {
+		return errmsg;
+	}
+	public void setErrmsg(String errmsg) {
+		this.errmsg = errmsg;
+	}
+	public String getData() {
+		return data;
+	}
+	public void setData(String data) {
+		this.data = data;
+	}
+	
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append("IpRegionIp360Response [errno=");
+		builder.append(errno);
+		builder.append(", errmsg=");
+		builder.append(errmsg);
+		builder.append(", data=");
+		builder.append(data);
+		builder.append("]");
+		return builder.toString();
+	}
+	
+}

+ 34 - 0
maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionIpchaxun.java

@@ -0,0 +1,34 @@
+package org.maxkey.web.ipregion;
+
+import java.io.IOException;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.select.Elements;
+
+public class IpRegionIpchaxun extends AbstractIpRegion implements IpRegion{
+	
+	public static final String REGION_URL = "https://ipchaxun.com/%s/";
+	
+	@Override
+	public String region(String ipAddress) {
+		try {
+			Document doc;
+			doc = Jsoup.connect(String.format(REGION_URL, ipAddress))
+					.timeout(TIMEOUT)
+					.userAgent(USERAGENT)
+					.header("Host","ipchaxun.com")
+					.header("Referer","https://ipchaxun.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();
+			
+			Elements address = doc.select(".info label span.value");
+			return address.get(1).text().trim();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+	
+}

+ 31 - 0
maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionIpcn.java

@@ -0,0 +1,31 @@
+package org.maxkey.web.ipregion;
+
+import java.io.IOException;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.select.Elements;
+
+public class IpRegionIpcn extends AbstractIpRegion implements IpRegion{
+	
+	public static final String REGION_URL = "https://ip.cn/ip/%s.html";
+	
+	@Override
+	public String region(String ipAddress) {
+		try {
+			Document doc;
+			doc = Jsoup.connect(String.format(REGION_URL, ipAddress))
+					.timeout(TIMEOUT)
+					.userAgent(USERAGENT)
+					.header("referer","https://ip.cn/")
+					.header("sec-ch-ua", "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"98\", \"Google Chrome\";v=\"98\"")
+					.get();
+			Elements address = doc.select("#tab0_address");
+			return address.text().trim();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+	
+}

+ 12 - 0
maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionLocal.java

@@ -0,0 +1,12 @@
+package org.maxkey.web.ipregion;
+
+public class IpRegionLocal extends AbstractIpRegion implements IpRegion{
+	
+	@Override
+	public String region(String ipAddress) {
+		if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
+			return "local";
+		}
+		return null;
+	}
+}

+ 18 - 0
maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionPconline.java

@@ -0,0 +1,18 @@
+package org.maxkey.web.ipregion;
+
+import org.maxkey.util.JsonUtils;
+import org.maxkey.web.HttpRequestAdapter;
+
+public class IpRegionPconline  extends AbstractIpRegion implements IpRegion{
+	
+	public static final String REGION_URL = "http://whois.pconline.com.cn/ipJson.jsp?json=true&ip=%s";
+	
+	@Override
+	public String region(String ipAddress) {
+		String responseJson  = 
+				new HttpRequestAdapter(HttpRequestAdapter.MediaType.JSON)
+								.get(String.format(REGION_URL,ipAddress),null);
+		return JsonUtils.json2Object(responseJson, IpRegionPconlineResponse.class).getAddr().trim();
+	}
+	
+}

+ 20 - 0
maxkey-core/src/main/java/org/maxkey/web/ipregion/IpRegionPconlineResponse.java

@@ -0,0 +1,20 @@
+package org.maxkey.web.ipregion;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class IpRegionPconlineResponse {
+
+	String addr;
+
+	public String getAddr() {
+		return addr;
+	}
+
+	public void setAddr(String addr) {
+		this.addr = addr;
+	}
+
+	public IpRegionPconlineResponse() {
+	}
+}

+ 1 - 0
maxkey-core/src/main/java/org/maxkey/web/ipregion/package-info.java

@@ -0,0 +1 @@
+package org.maxkey.web.ipregion;

+ 17 - 0
maxkey-core/src/test/java/org/maxkey/web/ipregion/IpRegionFactoryTest.java

@@ -0,0 +1,17 @@
+package org.maxkey.web.ipregion;
+
+import org.junit.Test;
+
+public class IpRegionFactoryTest {
+
+	@Test
+	public void test(){
+		System.out.println(IpRegionFactory.getFactory().getLocation(
+				IpRegionFactory.getFactory().region("127.0.0.1")
+				));
+		
+		System.out.println(IpRegionFactory.getFactory().getLocation(
+				IpRegionFactory.getFactory().region("117.155.70.59")
+				));
+	}
+}

+ 13 - 0
maxkey-core/src/test/java/org/maxkey/web/ipregion/IpRegionIp138Test.java

@@ -0,0 +1,13 @@
+package org.maxkey.web.ipregion;
+
+import org.junit.Test;
+
+public class IpRegionIp138Test {
+	
+	@Test
+	public void test(){
+		IpRegion ipRegion = new IpRegionIp138();
+		System.out.println(ipRegion.region("117.155.70.59"));
+	}
+	
+}

+ 13 - 0
maxkey-core/src/test/java/org/maxkey/web/ipregion/IpRegionIp360Test.java

@@ -0,0 +1,13 @@
+package org.maxkey.web.ipregion;
+
+import org.junit.Test;
+
+public class IpRegionIp360Test {
+	
+	@Test
+	public void test(){
+		IpRegion ipRegion = new IpRegionIp360();
+		System.out.println(ipRegion.region("117.155.70.59"));
+	}
+	
+}

+ 13 - 0
maxkey-core/src/test/java/org/maxkey/web/ipregion/IpRegionIpchaxunTest.java

@@ -0,0 +1,13 @@
+package org.maxkey.web.ipregion;
+
+import org.junit.Test;
+
+public class IpRegionIpchaxunTest {
+	
+	@Test
+	public void test(){
+		IpRegion ipRegion = new IpRegionIpchaxun();
+		System.out.println(ipRegion.region("117.155.70.59"));
+	}
+	
+}

+ 13 - 0
maxkey-core/src/test/java/org/maxkey/web/ipregion/IpRegionIpcnTest.java

@@ -0,0 +1,13 @@
+package org.maxkey.web.ipregion;
+
+import org.junit.Test;
+
+public class IpRegionIpcnTest {
+	
+	@Test
+	public void test(){
+		IpRegion ipRegion = new IpRegionIpcn();
+		System.out.println(ipRegion.region("117.155.70.59"));
+	}
+	
+}

+ 13 - 0
maxkey-core/src/test/java/org/maxkey/web/ipregion/IpRegionPconlineTest.java

@@ -0,0 +1,13 @@
+package org.maxkey.web.ipregion;
+
+import org.junit.Test;
+
+public class IpRegionPconlineTest {
+	
+	@Test
+	public void test(){
+		IpRegion ipRegion = new IpRegionPconline();
+		System.out.println(ipRegion.region("117.155.70.59"));
+	}
+	
+}