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
반응형
728x90
반응형

Locale 클래스를 이용한 표준 국가 명 한글로 얻어오기

java.util 패키지에 있는 Locale 클래스를 이용하게 되면 국가 정보를 얻을 수 가 있는데

그중 getISOCountries() 메소드를 이용하면 국제 표준 국가명 리스트를 배열로 가져올 수 있다. 

가져온 국가명 리스트를 한글로 변환 하기위해 Locale 클래스의 생성자인 Locale(String language, String country)

로 초기화후 getDisplayCountry() 메소드를 출력하면 결과가 출력된다.

1
2
3
4
5
6
7
8
9
10
11
 
public class LocaleTest {         
    public static void main(String[] args) {                
        java.util.Locale locale = null;                
        String[] countries = java.util.Locale.getISOCountries();                
        for(String country : countries) {            
            locale = new java.util.Locale("ko", country);            
            System.out.println(locale.getDisplayCountry());        
        }    
    }
cs



728x90
반응형
728x90
반응형

Quartz 라이브러리 연동을 통한 스케쥴러 구현

Apache Camel 프로젝트의 하위 프로젝트인 Quartz 이용하면 Unix 나 Linux 에서 사용하는 crontab 과 같은 스케쥴러를 손쉽게 구현하고 제어 할 수 있다. 

Quartz 는 기본적으로 log4j 를 사용하여 로그를 출력하기때문에 Quartz 사용시 log4j 라이브러리를 먼저 연동해줘야 정상적으로 사용이 가능하다 .

이번에 나름 독자적인 배치 모듈을 개발하다 보니 Quartz 를 상당히 유용하게 사용하였다. 

참조 라이브러리



1. JobScheduler 클래스를 구현 

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
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.TriggerBuilder;
 
 /**
 * Quartz 를 사용한 CronScheduler 구현
 * 
 * @author digimon1740
 * */
public class JobScheduler {
 
    String cronExp;                    
    Class<extends Job> clazz;
    
    public JobScheduler(Class<extends Job> clazz, String cronExp) {
        this.clazz = clazz;
        this.cronExp = cronExp;
    }
 
    public void triggerJob() {
        
        Scheduler sch = null;
        SchedulerFactory schFactory = null;
        
        // schedule the job
        //    this.clazz 는 기본적으로 JobExecuter 클래스이다.
        JobDetail job = JobBuilder.newJob(this.clazz)
                .withIdentity(this.clazz.getName()).build();
 
        // batch.properties 에서 크론 표현식을 가져와 Trigger를 생성
        CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                .withIdentity("crontrigger""crontriggergroup1")
                .withSchedule(CronScheduleBuilder.cronSchedule(this.cronExp))
                .build();
 
        try {
            schFactory = new service.StdSchedulerFactory();
            sch = schFactory.getScheduler();
            sch.start();    //    JobExecuter.class Start
            sch.scheduleJob(job, cronTrigger);
        } catch (SchedulerException e) {
        }
        
    }
 
     public static void main(String[] args) {
         // 매일 오전 2시에 수행
        JobScheduler js = new JobScheduler(JobExecuter.class"0 0 2 * * ?");
         js.triggerJob();
     }
 
}
 
 
cs


2.Job 인터페이스를 구현하여 execute() 메서드에 스케쥴링에 수행되는 코드를 작성한다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
 
/**
 *         Job 인터페이스의 execute() 를 구현함으로써 
 *         Quartz 의 스케쥴링을 이용할 수있다.
 * 
 *         @author digimon1740 
 * */
public class JobExecuter implements Job {
 
    @Override
    public void execute(JobExecutionContext jec) throws JobExecutionException {
        // / 스케쥴러에 의해 실행될 Job
        System.out.println("스케쥴러 수행중..");
    }
 
}
cs


728x90
반응형
728x90
반응형

간단한 배치모듈을 구성중 외부 설정파일(properties) 에 대해 원하는 키를 인자로 넣어 값을 리턴하는 클래스가 필요하여 

간단하게 구성한 클래스이다. 

이 클래스의 예제 에서는 getString(),getBoolean(),getLong(),getInt() 와 getObject()를 이용하여 설정값을 가져오고있다.

이를 응용하여 다른 자료형으로 반환하는 메서드를 만들어 사용해도 될것이다.


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
/**
 *      create by sanghoon lee
 *
 * */
package util;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
 
public class ConfigInjector {
 
    private static ConfigInjector config = new ConfigInjector();
    final static String CRLF = "\n\r";
    Properties props;
 
    private ConfigInjector() {
        init();
    }
 
    private void init() {
        loadConfig();
    }
 
    /**
     * properties load..
     * */
    public void loadConfig() {
        props = new Properties();
        try {
            if(isWindow()) {    //    윈도우인 경우..
                 props.load(
                         new BufferedReader(
                         new FileReader(
                         new File( "properties/batch.properties" )
                         )));
            }
            else {    //    윈도우가 아닌경우..
                props.load(new BufferedReader(new FileReader(System
                        .getProperty("batch.properties"))));    
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public boolean isWindow() {
        String os = System.getProperty("os.name");
        if(os.indexOf("Window") != -1) 
            return true;    //    윈도우 일경우 
        return false;    //    윈도우가 아닐경우
    }
    
    /**
     * load 한 프로퍼티 파싱
     * */
    public String parseConfig(String key) {
        String value = "";
        try {
            value = props.getProperty(key);
        } catch (Exception e) {
            // ignore
        }
        return value;
    }
 
    /**
     * propertis 를 읽어와 Object 으로 리턴 getObject 에서는
     * */
    public Object getObject(String key) {
        String value = "";
        try {
            value = parseConfig(key);
        } catch (Exception e) {
            // ignore
        }
        return value.trim();
    }
 
    /**
     * properties 를 읽어와 String 으로 리턴
     * */
    public String getString(String key) {
        String value = "";
        try {
            value = (String) getObject(key);
        } catch (ClassCastException e) {
            // ignore
        }
        return value;
    }
 
    /**
     * properties 를 읽어와 boolean 으로 리턴
     * */
    public boolean getBoolean(String key) {
        boolean flag = false;
        try {
            flag = Boolean.parseBoolean((String) getObject(key));
        } catch (ClassCastException e) {
            // ignore
        }
        return flag;
    }
 
    /**
     * properties 를 읽어와 int 으로 리턴
     * */
    public int getInt(String key) {
        int result = 0;
        try {
            result = Integer.parseInt((String) getObject(key));
        } catch (ClassCastException e) {
            // ignore
        }
        return result;
    }
 
    /**
     * properties 를 읽어와 Long 으로 리턴
     * */
    public long getLong(String key) {
        long result = 0;
        try {
            result = Long.parseLong((String) getObject(key));
        } catch (ClassCastException e) {
            // ignore
        }
        return result;
    }
 
    /**
     * ConfigInjector 인스턴스를 리턴
     * */
    public static ConfigInjector getInstance() {
        return config;
    }
 
    /*
     * public static void main( String[] args ) { ConfigInjector config =
     * ConfigInjector.getInstance(); String pw =
     * config.getString("copydb.userpw"); String dir =
     * config.getString("batchjob.log.dir"); System.out.println( pw );
     * System.out.println( dir ); ConfigEntity entity =
     * config.getConfigEntity(); System.out.println(entity); }
     */
 
}


728x90
반응형

+ Recent posts