728x90
반응형

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


위의 라이브러리를 모두 프로젝트에 BuildPath 시킨 후 IP 별 국가 정보가 있는 무료 DB 를 다운로드 받아야한다.
다운로드 주소 http://dev.maxmind.com/geoip/geoip2/geolite2/

모든 라이브러리와 데이터베이스를 다운받은후 라이브러리는 이클립스에 빌드패스를 하고 

다운받은 데이터베이스는  프로젝트 의 경로로 이동 시킨뒤  아래처럼 예제 소스를 작성해보았다. 

GeoIP2.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
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 체크를 통해 해결해야 할것이다. 







728x90
반응형

+ Recent posts