Skip to content

Commit 40ec216

Browse files
authored
Merge pull request #309 from KGU-C-Lab/refactor/#299
GeoIP2 -> IPInfo 서비스 변경
2 parents d4fd9b5 + 6e78e7d commit 40ec216

File tree

11 files changed

+110
-42
lines changed

11 files changed

+110
-42
lines changed

src/main/java/page/clab/api/domain/book/dao/BookLoanRecordRepositoryImpl.java

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public Page<BookLoanRecordResponseDto> findByConditions(Long bookId, String borr
3434
List<BookLoanRecordResponseDto> results = queryFactory
3535
.select(Projections.constructor(
3636
BookLoanRecordResponseDto.class,
37+
bookLoanRecord.id,
3738
bookLoanRecord.book.id,
3839
bookLoanRecord.book.title,
3940
bookLoanRecord.book.imageUrl,

src/main/java/page/clab/api/domain/book/dto/response/BookLoanRecordResponseDto.java

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
@AllArgsConstructor
1313
public class BookLoanRecordResponseDto {
1414

15+
private Long bookLoanRecordId;
16+
1517
private Long bookId;
1618

1719
private String bookTitle;

src/main/java/page/clab/api/domain/login/application/LoginAttemptLogService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import page.clab.api.domain.login.dto.response.LoginAttemptLogResponseDto;
1414
import page.clab.api.global.common.dto.PagedResponseDto;
1515
import page.clab.api.global.util.HttpReqResUtil;
16-
import page.clab.api.global.util.IpInfoUtil;
16+
import page.clab.api.global.util.IPInfoUtil;
1717

1818
@Service
1919
@RequiredArgsConstructor
@@ -24,7 +24,7 @@ public class LoginAttemptLogService {
2424
@Transactional
2525
public void createLoginAttemptLog(HttpServletRequest request, String memberId, LoginAttemptResult loginAttemptResult) {
2626
String clientIpAddress = HttpReqResUtil.getClientIpAddressIfServletRequestExist();
27-
IPResponse ipResponse = IpInfoUtil.getIpInfo(request);
27+
IPResponse ipResponse = HttpReqResUtil.isLocalRequest(clientIpAddress) ? null : IPInfoUtil.getIpInfo(request);
2828
LoginAttemptLog loginAttemptLog = LoginAttemptLog.create(memberId, request, clientIpAddress, ipResponse, loginAttemptResult);
2929
loginAttemptLogRepository.save(loginAttemptLog);
3030
}

src/main/java/page/clab/api/domain/login/domain/LoginAttemptLog.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public static LoginAttemptLog create(String memberId, HttpServletRequest httpSer
4848
.memberId(memberId)
4949
.userAgent(httpServletRequest.getHeader("User-Agent"))
5050
.ipAddress(ipAddress)
51-
.location(ipResponse == null ? "Unknown" : ipResponse.getCountryName() + " " + ipResponse.getRegion() + " " + ipResponse.getCity())
51+
.location(ipResponse == null ? "Unknown" : ipResponse.getCity())
5252
.loginAttemptResult(loginAttemptResult)
5353
.loginAttemptTime(LocalDateTime.now())
5454
.build();
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package page.clab.api.global.auth.filter;
22

3+
import io.ipinfo.api.IPinfo;
4+
import io.ipinfo.api.errors.RateLimitedException;
35
import io.ipinfo.api.model.IPResponse;
6+
import io.ipinfo.spring.strategies.attribute.AttributeStrategy;
7+
import io.ipinfo.spring.strategies.interceptor.InterceptorStrategy;
48
import jakarta.servlet.Filter;
59
import jakarta.servlet.FilterChain;
610
import jakarta.servlet.FilterConfig;
@@ -10,35 +14,75 @@
1014
import jakarta.servlet.http.HttpServletRequest;
1115
import lombok.extern.slf4j.Slf4j;
1216
import org.springframework.stereotype.Component;
17+
import page.clab.api.global.config.IPInfoConfig;
1318
import page.clab.api.global.util.HttpReqResUtil;
14-
import page.clab.api.global.util.IpInfoUtil;
1519

1620
import java.io.IOException;
1721

1822
@Component
1923
@Slf4j
2024
public class IpAuthenticationFilter implements Filter {
2125

26+
private final IPinfo ipInfo;
27+
28+
private final AttributeStrategy attributeStrategy;
29+
30+
private final InterceptorStrategy interceptorStrategy;
31+
32+
public IpAuthenticationFilter(IPInfoConfig ipInfoConfig) {
33+
ipInfo = ipInfoConfig.ipInfo();
34+
attributeStrategy = ipInfoConfig.attributeStrategy();
35+
interceptorStrategy = ipInfoConfig.interceptorStrategy();
36+
}
37+
2238
@Override
2339
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
24-
String ipAddress = HttpReqResUtil.getClientIpAddressIfServletRequestExist();
25-
IPResponse ipResponse = IpInfoUtil.getIpInfo((HttpServletRequest) request);
26-
String country = ipResponse == null ? null : ipResponse.getCountryCode();
27-
if (country != null && !country.equals("KR")) {
28-
log.info("[{}:{}] 허용되지 않은 국가로부터의 접근입니다.", ipAddress, country);
29-
return;
40+
HttpServletRequest httpRequest = (HttpServletRequest) request;
41+
String clientIpAddress = HttpReqResUtil.getClientIpAddressIfServletRequestExist();
42+
try {
43+
if (shouldProcessRequest(httpRequest, clientIpAddress)) {
44+
chain.doFilter(request, response);
45+
return;
46+
}
47+
IPResponse ipResponse = storeIpInformation(clientIpAddress, httpRequest);
48+
if (isNonPermittedCountry(ipResponse)) {
49+
log.warn("Access from non-permitted country: {}", ipResponse.getCountryCode());
50+
return;
51+
}
52+
} catch (RateLimitedException e) {
53+
log.error("Rate limit exceeded while getting IP information.");
54+
} catch (Exception e) {
55+
log.error("Failed to get IP information.");
3056
}
3157
chain.doFilter(request, response);
3258
}
3359

60+
private boolean shouldProcessRequest(HttpServletRequest httpRequest, String clientIpAddress) {
61+
return !interceptorStrategy.shouldRun(httpRequest)
62+
|| attributeStrategy.hasAttribute(httpRequest)
63+
|| HttpReqResUtil.isLocalRequest(clientIpAddress)
64+
|| clientIpAddress.equals("0.0.0.0");
65+
}
66+
67+
private IPResponse storeIpInformation(String clientIpAddress, HttpServletRequest httpRequest) throws RateLimitedException {
68+
IPResponse ipResponse = ipInfo.lookupIP(clientIpAddress);
69+
attributeStrategy.storeAttribute(httpRequest, ipResponse);
70+
return ipResponse;
71+
}
72+
73+
private boolean isNonPermittedCountry(IPResponse ipResponse) {
74+
String country = ipResponse.getCountryCode();
75+
return country != null && !country.equals("KR");
76+
}
77+
3478
@Override
3579
public void init(FilterConfig filterConfig) {
36-
log.info("IP Authentication Filter Init..");
80+
log.info("IP Authentication Filter initialized.");
3781
}
3882

3983
@Override
4084
public void destroy() {
41-
log.info("IP Authentication Filter Destroy..");
85+
log.info("IP Authentication Filter destroyed.");
4286
}
4387

4488
}

src/main/java/page/clab/api/global/common/dto/IpInfoResponse.java renamed to src/main/java/page/clab/api/global/common/dto/IPInfoResponse.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
@Setter
77
@Getter
8-
public class IpInfoResponse {
8+
public class IPInfoResponse {
99

1010
private String ip;
1111

src/main/java/page/clab/api/global/config/IPInfoConfig.java

+27-9
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,52 @@
55
import io.ipinfo.spring.strategies.attribute.AttributeStrategy;
66
import io.ipinfo.spring.strategies.attribute.SessionAttributeStrategy;
77
import io.ipinfo.spring.strategies.interceptor.BotInterceptorStrategy;
8+
import io.ipinfo.spring.strategies.interceptor.InterceptorStrategy;
9+
import io.ipinfo.spring.strategies.ip.IPStrategy;
810
import io.ipinfo.spring.strategies.ip.XForwardedForIPStrategy;
11+
import lombok.Getter;
912
import org.springframework.beans.factory.annotation.Value;
1013
import org.springframework.context.annotation.Bean;
1114
import org.springframework.context.annotation.Configuration;
15+
import org.springframework.web.client.RestClient;
1216

17+
@Getter
1318
@Configuration
1419
public class IPInfoConfig {
1520

21+
private final RestClient restClient = RestClient.create("https://ipinfo.io/");
22+
1623
@Value("${ipinfo.access-token}")
17-
private static String accessToken;
24+
private String accessToken;
1825

1926
@Bean
2027
public IPinfoSpring ipinfoSpring() {
2128
return new IPinfoSpring.Builder()
22-
.setIPinfo(new IPinfo.Builder().setToken(accessToken).build())
23-
.interceptorStrategy(new BotInterceptorStrategy())
24-
.ipStrategy(new XForwardedForIPStrategy())
25-
.attributeStrategy(new SessionAttributeStrategy())
29+
.setIPinfo(ipInfo())
30+
.interceptorStrategy(interceptorStrategy())
31+
.ipStrategy(ipStrategy())
32+
.attributeStrategy(attributeStrategy())
2633
.build();
2734
}
2835

2936
@Bean
30-
public AttributeStrategy attributeStrategy() {
31-
return new SessionAttributeStrategy();
37+
public IPinfo ipInfo() {
38+
return new IPinfo.Builder().setToken(accessToken).build();
39+
}
40+
41+
@Bean
42+
public InterceptorStrategy interceptorStrategy() {
43+
return new BotInterceptorStrategy();
3244
}
3345

34-
public String getAccessToken() {
35-
return accessToken;
46+
@Bean
47+
public IPStrategy ipStrategy() {
48+
return new XForwardedForIPStrategy();
49+
}
50+
51+
@Bean
52+
public AttributeStrategy attributeStrategy() {
53+
return new SessionAttributeStrategy();
3654
}
3755

3856
}

src/main/java/page/clab/api/global/config/SecurityConfig.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ public class SecurityConfig {
6262

6363
private final OpenApiPatternsProperties OpenApiPatternsProperties;
6464

65+
private final IPInfoConfig ipInfoConfig;
66+
6567
@Value("${resource.file.url}")
6668
String fileURL;
6769

@@ -79,7 +81,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
7981
.authorizeRequests(this::configureRequests)
8082
.authenticationProvider(authenticationConfig.authenticationProvider())
8183
.addFilterBefore(
82-
new IpAuthenticationFilter(),
84+
new IpAuthenticationFilter(ipInfoConfig),
8385
UsernamePasswordAuthenticationFilter.class
8486
)
8587
.addFilterBefore(

src/main/java/page/clab/api/global/config/WebConfig.java

-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package page.clab.api.global.config;
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
4-
import io.ipinfo.spring.IPinfoSpring;
54
import lombok.RequiredArgsConstructor;
65
import lombok.extern.slf4j.Slf4j;
76
import org.springframework.beans.factory.annotation.Autowired;
@@ -30,9 +29,6 @@ public class WebConfig implements WebMvcConfigurer {
3029
@Autowired
3130
private ApiLoggingInterceptor apiLoggingInterceptor;
3231

33-
@Autowired
34-
private IPinfoSpring ipinfoSpring;
35-
3632
@Value("${resource.file.path}")
3733
private String filePath;
3834

@@ -67,7 +63,6 @@ public MappingJackson2HttpMessageConverter jsonEscapeConverter() {
6763

6864
@Override
6965
public void addInterceptors(InterceptorRegistry registry) {
70-
registry.addInterceptor(ipinfoSpring);
7166
registry.addInterceptor(apiLoggingInterceptor);
7267
}
7368

src/main/java/page/clab/api/global/util/HttpReqResUtil.java

+10-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
import org.springframework.web.context.request.RequestContextHolder;
55
import org.springframework.web.context.request.ServletRequestAttributes;
66

7+
import java.util.Set;
8+
79
public class HttpReqResUtil {
810

11+
private static final Set<String> localIpSet = Set.of("0:0:0:0:0:0:0:1", "127.0.0.1", "192.168.0.1");
12+
913
private static final String[] IP_HEADER_CANDIDATES = {
1014
"X-Forwarded-For",
1115
"Proxy-Client-IP",
@@ -27,12 +31,15 @@ public static String getClientIpAddressIfServletRequestExist() {
2731
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
2832
for (String header : IP_HEADER_CANDIDATES) {
2933
String ipList = request.getHeader(header);
30-
if (ipList != null && ipList.length() != 0 && !"unknown".equalsIgnoreCase(ipList)) {
31-
String ip = ipList.split(",")[0];
32-
return ip;
34+
if (ipList != null && !ipList.isEmpty() && !"unknown".equalsIgnoreCase(ipList)) {
35+
return ipList.split(",")[0];
3336
}
3437
}
3538
return request.getRemoteAddr();
3639
}
3740

41+
public static boolean isLocalRequest(String ipAddress) {
42+
return localIpSet.contains(ipAddress);
43+
}
44+
3845
}

src/main/java/page/clab/api/global/util/IpInfoUtil.java renamed to src/main/java/page/clab/api/global/util/IPInfoUtil.java

+10-11
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,39 @@
44
import io.ipinfo.spring.strategies.attribute.AttributeStrategy;
55
import jakarta.servlet.http.HttpServletRequest;
66
import lombok.extern.slf4j.Slf4j;
7-
import org.springframework.beans.factory.annotation.Value;
87
import org.springframework.http.HttpStatusCode;
98
import org.springframework.http.MediaType;
109
import org.springframework.stereotype.Component;
1110
import org.springframework.web.client.RestClient;
12-
import page.clab.api.global.common.dto.IpInfoResponse;
11+
import page.clab.api.global.common.dto.IPInfoResponse;
1312
import page.clab.api.global.config.IPInfoConfig;
1413

1514
@Slf4j
1615
@Component
17-
public class IpInfoUtil {
16+
public class IPInfoUtil {
1817

19-
private static final RestClient restClient = RestClient.create();
20-
21-
private static final String IPINFO_BASE_URL = "https://ipinfo.io/";
18+
private static RestClient restClient;
2219

2320
private static String accessToken;
2421

2522
private static AttributeStrategy attributeStrategy;
2623

27-
public IpInfoUtil(IPInfoConfig ipInfoConfig) {
24+
public IPInfoUtil(IPInfoConfig ipInfoConfig) {
25+
restClient = ipInfoConfig.getRestClient();
2826
accessToken = ipInfoConfig.getAccessToken();
29-
IpInfoUtil.attributeStrategy = ipInfoConfig.attributeStrategy();
27+
IPInfoUtil.attributeStrategy = ipInfoConfig.attributeStrategy();
3028
}
3129

32-
public static IpInfoResponse getIpInfo(String ipAddress) {
30+
public static IPInfoResponse getIpInfo(String ipAddress) {
3331
return restClient.get()
34-
.uri(IPINFO_BASE_URL + ipAddress + "?token=" + accessToken)
32+
.uri(ipAddress)
33+
.header("Authorization", "Bearer " + accessToken)
3534
.accept(MediaType.APPLICATION_JSON)
3635
.retrieve()
3736
.onStatus(HttpStatusCode::is4xxClientError, ((request, response) -> {
3837
log.warn("4xx error occurred while getting ip info. Status code: {}", response.getStatusCode());
3938
}))
40-
.body(IpInfoResponse.class);
39+
.body(IPInfoResponse.class);
4140
}
4241

4342
public static IPResponse getIpInfo(HttpServletRequest request) {

0 commit comments

Comments
 (0)