MAXMIND GeoIP를 이용한 클라이언트 접속 국가 정보 확인
오늘은 MAXMIND API 를 이용하여 클라이언트의 접속 국가 정보를 확인 하는 방법에 대해 포스팅 하려한다.
우선 MAXMIND 에 대해 간략이 소개하겠다.
MAXMIND 는 전세계 IP 데이터베이스 서비스를 제공하는 업체이며 해당하는 IP 의 좌표(위도,경도) 정보를 제공한다.
무료버전 , 유료 버전 두가지 버전이 존재하며 무료버전은 국가 수준의 정보까지 제공하는 반면 유료버전은 도시 수준의 상세한 정보를 얻을 수 있다고한다.
요즘 해외 IP 로 서비스에 로그인하여 해킹시도 라던지 개인정보가 유출되는 문제가 많은데 이 서비스를 이용하여 그런 보안 문제를 해결 할 수 있을것으로보인다.
아래는 MAXMIND 사이트의 모습이다.
MAXMIND GeoIP 는 몇가지 언어의 API 가 제공된다.
공식으로 제공하는 언어의 API 는 현재 .NET, Java, Perl, PHP, Python 그리고 서드파티 API 로 Node.js , Ruby 총 7가지 언어로 개발이 가능하다.
오늘은 그중 JAVA 로 GeoIP 를 구현해 보려한다.
GeoIP 는 http://dev.maxmind.com/geoip/geoip2/downloadable 에서 다운로드가 가능하며
GeoIP 라이브러리는 몇가지 라이브러리를 의존한다.
의존 라이브러리
+- geoip2-0.7.0.jar
\- maxmind-db-0.3.1.jar
\- jackson-databind-2.2.3.jar
+- jackson-annotations-2.2.3.jar
\- jackson-core-2.2.3.jar
모든 라이브러리와 데이터베이스를 다운받은후 라이브러리는 이클립스에 빌드패스를 하고
다운받은 데이터베이스는 프로젝트 의 경로로 이동 시킨뒤 아래처럼 예제 소스를 작성해보았다.
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import com.maxmind.geoip2.DatabaseReader; import com.maxmind.geoip2.exception.GeoIp2Exception; import com.maxmind.geoip2.model.CityResponse; import com.maxmind.geoip2.record.Subdivision; public class GeoIP2 { private static final String RESOURCE_NAME = "GeoLite2-City.mmdb"; private static GeoIP2 INSTANCE = null; private DatabaseReader reader = null; private GeoIP2() throws InstantiationException { init(); } private void init() throws InstantiationException { String path = System.getProperty("com.maxmind.geoip2.database.file"); try { File file = new File(path); this.reader = newDatabaseReader(file); StringBuilder sb = new StringBuilder(); sb.append("INFO: MaxMind GeoIP2 DatabaseReader initiated."); sb.append(System.getProperty("line.separator")); sb.append("INFO: Database File: ").append(file.getAbsolutePath()); System.out.println(sb.toString()); return; } catch (Exception e) { java.io.StringWriter sw = new java.io.StringWriter(); sw.write("ERROR: Failed to initiate MaxMind GeoIP2 DatabaseReader"); sw.write(System.getProperty("line.separator")); sw.write("${com.maxmind.geoip2.database.file}: "); sw.write(path); sw.write(System.getProperty("line.separator")); e.printStackTrace(new java.io.PrintWriter(sw)); System.out.println(sw.toString()); } this.reader = newDatabaseReader(); StringBuilder sb = new StringBuilder(); sb.append("INFO: MaxMind GeoIP2 DatabaseReader initiated."); sb.append(System.getProperty("line.separator")); sb.append("INFO: ").append(this.getClass().getName()).append(".class"); sb.append(".getResourceAsStream(\"").append(RESOURCE_NAME); sb.append("\")").append(System.getProperty("line.separator")); System.out.println(sb.toString()); return; } private DatabaseReader newDatabaseReader(File file) throws InstantiationException { DatabaseReader reader = null; try { reader = new DatabaseReader.Builder(file).build(); } catch (Exception e) { InstantiationException exception = new InstantiationException( "Failed to create GeoIP2 DatabaseReader from file: " + file.getAbsolutePath()); exception.initCause(e); throw exception; } return reader; } private DatabaseReader newDatabaseReader() throws InstantiationException { DatabaseReader reader = null; InputStream stream = this.getClass().getResourceAsStream(RESOURCE_NAME); try { reader = new DatabaseReader.Builder(stream).build(); } catch (Exception e) { InstantiationException exception = new InstantiationException( "Failed to create GeoIP2 DatabaseReader from resource: " + RESOURCE_NAME); exception.initCause(e); throw exception; } finally { try { if (stream != null) stream.close(); } catch (Exception e) { // ignore } } return reader; } public static GeoIP2 getInstance() throws InstantiationException { if (INSTANCE != null) { return INSTANCE; } synchronized (GeoIP2.class) { if (INSTANCE == null) INSTANCE = new GeoIP2(); } return INSTANCE; } public LookupResult lookup(String host) throws IOException, GeoIp2Exception { InetAddress ipAddress = InetAddress.getByName(host); CityResponse response = null; synchronized (reader) { response = reader.city(ipAddress); } if (response == null) { return null; } LookupResult result = new LookupResult(); result.setIPAddress(ipAddress.getHostAddress()); result.setCountryCode(response.getCountry().getIsoCode()); // 'US' result.setCountryName(response.getCountry().getName()); // 'United States' // response.getCountry().getNames().get("zh-CN")); // '美國' Subdivision subdivision = response.getMostSpecificSubdivision(); result.setSubdivisionCode(subdivision.getIsoCode()); // 'MN' result.setSubdivisionName(subdivision.getName()); // 'Minnesota' result.setCity(response.getCity().getName()); // 'Minneapolis' result.setPostalCode(response.getPostal().getCode()); // '55455' result.setLatitude(response.getLocation().getLatitude()); // 44.9733 result.setLongitude(response.getLocation().getLongitude()); // -93.2323 return result; } } |
LookupResult.java
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | public class LookupResult { private String ipAddress; private String continentCode; private String continentName; private String countryCode; private String countryName; private String subdivisionCode; private String subdivisionName; private String city; private String postalCode; private double latitude; private double longitude; public LookupResult() { super(); } public String getIPAddress() { return ipAddress; } void setIPAddress(String ipAddress) { this.ipAddress = ipAddress; } /** * @return A two character continent code like "NA" (North America) or "OC" * (Oceania). This attribute is returned by all end points. */ public String getContinentCode() { return continentCode; } void setContinentCode(String continentCode) { this.continentCode = continentCode; } /** * @return The continent name associated with an IP address. This attribute * is returned by all the end points. */ public String getContinentName() { return continentName; } void setContinentName(String continentName) { this.continentName = continentName; } /** * @return The <a * href="http://en.wikipedia.org/wiki/ISO_3166-1">two-character ISO * 3166-1 alpha code</a> for the country. This attribute is returned * by all end points. */ public String getCountryCode() { return countryCode; } void setCountryCode(String countryCode) { this.countryCode = countryCode; } /** * @return The country name associated with an IP address. This attribute is * returned by all the end points. */ public String getCountryName() { return countryName; } void setCountryName(String countryName) { this.countryName = countryName; } /** * @return This is a string up to three characters long contain the * subdivision portion of the <a * href="http://en.wikipedia.org/wiki/ISO_3166-2 ISO 3166-2" * >code</a>. This attribute is returned by all end points except * Country. */ public String getSubdivisionCode() { return subdivisionCode; } void setSubdivisionCode(String subdivisionCode) { this.subdivisionCode = subdivisionCode; } /** * @return The subdivision name associated with an IP address. This * attribute is returned by all the end points except the Country * end point. */ public String getSubdivisionName() { return subdivisionName; } void setSubdivisionName(String subdivisionName) { this.subdivisionName = subdivisionName; } /** * @return The city associated with an IP address. This attribute is * returned by all the end points except the Country end point. */ public String getCity() { return city; } void setCity(String city) { this.city = city; } /** * @return The city associated with an IP address. This attribute is * returned by all the end points except the Country end point. */ public String getPostalCode() { return postalCode; } void setPostalCode(String postalCode) { this.postalCode = postalCode; } /** * @return The latitude of the location as a floating point number. This * attribute is returned by all end points except the Country end * point. */ public double getLatitude() { return latitude; } void setLatitude(double latitude) { this.latitude = latitude; } /** * @return The longitude of the location as a floating point number. This * attribute is returned by all end points except the Country end * point. */ public double getLongitude() { return longitude; } void setLongitude(double longitude) { this.longitude = longitude; } public String toSring() { String LS = System.getProperty("line.separator"); StringBuffer sb = new StringBuffer(); sb.append(super.toString()).append(" {").append(LS); sb.append('\t'); sb.append("ipAddress: \"").append(ipAddress).append('"').append(LS); sb.append('\t'); sb.append("countryCode: \"").append(countryCode).append('"').append(LS); sb.append('\t'); sb.append("countryName: \"").append(countryName).append('"').append(LS); sb.append('\t'); sb.append("subdivisionCode: \"").append(subdivisionCode).append('"'); sb.append(LS).append('\t'); sb.append("subdivisionName: \"").append(subdivisionName).append('"'); sb.append(LS).append('\t'); sb.append("city: \"").append(city).append('"').append(LS); sb.append('\t'); sb.append("postalCode: \"").append(postalCode).append('"').append(LS); sb.append('\t'); sb.append("latitude: ").append(latitude).append(LS); sb.append('\t'); sb.append("longitude: ").append(longitude).append(LS); sb.append('}'); return sb.toString(); } } |
GeoTest.java
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 | public class GeoTest { protected static void testGeoIP2() throws Exception { GeoIP2 geoIP = GeoIP2.getInstance(); java.util.Locale locale = new java.util.Locale("ko", "KR"); LookupResult result = geoIP.lookup("206.190.36.45"); System.out.println(result.toSring()); locale = new java.util.Locale("ko", result.getCountryCode()); System.out.println("국가명 : " + locale.getDisplayCountry()); result = geoIP.lookup("2001:2B8:2:FFF3::100"); System.out.println(result.toSring()); locale = new java.util.Locale("ko", result.getCountryCode()); System.out.println("국가명 : " + locale.getDisplayCountry()); result = geoIP.lookup("175.223.22.42"); System.out.println(result.toSring()); locale = new java.util.Locale("ko", result.getCountryCode()); System.out.println("국가명 : " + locale.getDisplayCountry()); } public static void main(String[] args) throws Exception { //String dbFile = "C:/Users/assembly/Desktop/GeoIP2-java-master/GeoIP2-java-master/src/test/resources/maxmind-db/GeoLite2-City.mmdb"; String dbFile = "C:/Users/workspace/geoIp/src/GeoLite2-City.mmdb"; System.setProperty("com.maxmind.geoip2.database.file", dbFile); testGeoIP2(); } } |
[출력결과]
위의 결과 처럼 IP 정보로 국가명과 좌표(위도,경도) 정보를 가져오는 것을 확인 할 수있다.
이건 꼼수인데 무상 API 이다 보니 국가정보외엔 NULL 값이 추출되지만 해당 좌표를 이용해서 Google Map 과 연동하면
어느정도 근사치의 도시정보를 가져올 수 있을것으로 보인다.
하지만 한국에서는 도시 위치정보는 정확하지 않을 수 있다 오차 30km 정도 된다고 하는데 실제로 Google Map 과 연동시 ISP 의 위치가 표현되는걸 확인했다.
그리고 추가로 사설 IP 인경우에는 당연하겠지만 주소가 나오지 않고 Exception 이 발생한다.
이는 개발시 예외 처리나 NULL 체크를 통해 해결해야 할것이다.
'JAVA' 카테고리의 다른 글
Runtime 클래스를 이용한 외부파일 실행 (0) | 2014.09.30 |
---|---|
Lombok 을 이용한 getter,setter 자동생성 (0) | 2014.08.28 |
Locale 클래스를 이용한 표준 국가 명 한글로 얻어오기 (1) | 2014.07.01 |
Quartz 라이브러리 연동을 통한 스케쥴러 구현 (0) | 2014.06.06 |
외부 설정파일을 읽어들여 값을 반환하는 ConfigInjector 클래스 (0) | 2014.05.06 |