Skip to content

Commit

Permalink
Merge pull request #309 from KGU-C-Lab/refactor/#299
Browse files Browse the repository at this point in the history
GeoIP2 -> IPInfo 서비스 변경
  • Loading branch information
limehee authored Apr 26, 2024
2 parents 8347779 + 951396f commit e4d6d53
Show file tree
Hide file tree
Showing 11 changed files with 110 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public Page<BookLoanRecordResponseDto> findByConditions(Long bookId, String borr
List<BookLoanRecordResponseDto> results = queryFactory
.select(Projections.constructor(
BookLoanRecordResponseDto.class,
bookLoanRecord.id,
bookLoanRecord.book.id,
bookLoanRecord.book.title,
bookLoanRecord.book.imageUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
@AllArgsConstructor
public class BookLoanRecordResponseDto {

private Long bookLoanRecordId;

private Long bookId;

private String bookTitle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import page.clab.api.domain.login.dto.response.LoginAttemptLogResponseDto;
import page.clab.api.global.common.dto.PagedResponseDto;
import page.clab.api.global.util.HttpReqResUtil;
import page.clab.api.global.util.IpInfoUtil;
import page.clab.api.global.util.IPInfoUtil;

@Service
@RequiredArgsConstructor
Expand All @@ -24,7 +24,7 @@ public class LoginAttemptLogService {
@Transactional
public void createLoginAttemptLog(HttpServletRequest request, String memberId, LoginAttemptResult loginAttemptResult) {
String clientIpAddress = HttpReqResUtil.getClientIpAddressIfServletRequestExist();
IPResponse ipResponse = IpInfoUtil.getIpInfo(request);
IPResponse ipResponse = HttpReqResUtil.isLocalRequest(clientIpAddress) ? null : IPInfoUtil.getIpInfo(request);
LoginAttemptLog loginAttemptLog = LoginAttemptLog.create(memberId, request, clientIpAddress, ipResponse, loginAttemptResult);
loginAttemptLogRepository.save(loginAttemptLog);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static LoginAttemptLog create(String memberId, HttpServletRequest httpSer
.memberId(memberId)
.userAgent(httpServletRequest.getHeader("User-Agent"))
.ipAddress(ipAddress)
.location(ipResponse == null ? "Unknown" : ipResponse.getCountryName() + " " + ipResponse.getRegion() + " " + ipResponse.getCity())
.location(ipResponse == null ? "Unknown" : ipResponse.getCity())
.loginAttemptResult(loginAttemptResult)
.loginAttemptTime(LocalDateTime.now())
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package page.clab.api.global.auth.filter;

import io.ipinfo.api.IPinfo;
import io.ipinfo.api.errors.RateLimitedException;
import io.ipinfo.api.model.IPResponse;
import io.ipinfo.spring.strategies.attribute.AttributeStrategy;
import io.ipinfo.spring.strategies.interceptor.InterceptorStrategy;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
Expand All @@ -10,35 +14,75 @@
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import page.clab.api.global.config.IPInfoConfig;
import page.clab.api.global.util.HttpReqResUtil;
import page.clab.api.global.util.IpInfoUtil;

import java.io.IOException;

@Component
@Slf4j
public class IpAuthenticationFilter implements Filter {

private final IPinfo ipInfo;

private final AttributeStrategy attributeStrategy;

private final InterceptorStrategy interceptorStrategy;

public IpAuthenticationFilter(IPInfoConfig ipInfoConfig) {
ipInfo = ipInfoConfig.ipInfo();
attributeStrategy = ipInfoConfig.attributeStrategy();
interceptorStrategy = ipInfoConfig.interceptorStrategy();
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String ipAddress = HttpReqResUtil.getClientIpAddressIfServletRequestExist();
IPResponse ipResponse = IpInfoUtil.getIpInfo((HttpServletRequest) request);
String country = ipResponse == null ? null : ipResponse.getCountryCode();
if (country != null && !country.equals("KR")) {
log.info("[{}:{}] 허용되지 않은 국가로부터의 접근입니다.", ipAddress, country);
return;
HttpServletRequest httpRequest = (HttpServletRequest) request;
String clientIpAddress = HttpReqResUtil.getClientIpAddressIfServletRequestExist();
try {
if (shouldProcessRequest(httpRequest, clientIpAddress)) {
chain.doFilter(request, response);
return;
}
IPResponse ipResponse = storeIpInformation(clientIpAddress, httpRequest);
if (isNonPermittedCountry(ipResponse)) {
log.warn("Access from non-permitted country: {}", ipResponse.getCountryCode());
return;
}
} catch (RateLimitedException e) {
log.error("Rate limit exceeded while getting IP information.");
} catch (Exception e) {
log.error("Failed to get IP information.");
}
chain.doFilter(request, response);
}

private boolean shouldProcessRequest(HttpServletRequest httpRequest, String clientIpAddress) {
return !interceptorStrategy.shouldRun(httpRequest)
|| attributeStrategy.hasAttribute(httpRequest)
|| HttpReqResUtil.isLocalRequest(clientIpAddress)
|| clientIpAddress.equals("0.0.0.0");
}

private IPResponse storeIpInformation(String clientIpAddress, HttpServletRequest httpRequest) throws RateLimitedException {
IPResponse ipResponse = ipInfo.lookupIP(clientIpAddress);
attributeStrategy.storeAttribute(httpRequest, ipResponse);
return ipResponse;
}

private boolean isNonPermittedCountry(IPResponse ipResponse) {
String country = ipResponse.getCountryCode();
return country != null && !country.equals("KR");
}

@Override
public void init(FilterConfig filterConfig) {
log.info("IP Authentication Filter Init..");
log.info("IP Authentication Filter initialized.");
}

@Override
public void destroy() {
log.info("IP Authentication Filter Destroy..");
log.info("IP Authentication Filter destroyed.");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

@Setter
@Getter
public class IpInfoResponse {
public class IPInfoResponse {

private String ip;

Expand Down
36 changes: 27 additions & 9 deletions src/main/java/page/clab/api/global/config/IPInfoConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,52 @@
import io.ipinfo.spring.strategies.attribute.AttributeStrategy;
import io.ipinfo.spring.strategies.attribute.SessionAttributeStrategy;
import io.ipinfo.spring.strategies.interceptor.BotInterceptorStrategy;
import io.ipinfo.spring.strategies.interceptor.InterceptorStrategy;
import io.ipinfo.spring.strategies.ip.IPStrategy;
import io.ipinfo.spring.strategies.ip.XForwardedForIPStrategy;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;

@Getter
@Configuration
public class IPInfoConfig {

private final RestClient restClient = RestClient.create("https://ipinfo.io/");

@Value("${ipinfo.access-token}")
private static String accessToken;
private String accessToken;

@Bean
public IPinfoSpring ipinfoSpring() {
return new IPinfoSpring.Builder()
.setIPinfo(new IPinfo.Builder().setToken(accessToken).build())
.interceptorStrategy(new BotInterceptorStrategy())
.ipStrategy(new XForwardedForIPStrategy())
.attributeStrategy(new SessionAttributeStrategy())
.setIPinfo(ipInfo())
.interceptorStrategy(interceptorStrategy())
.ipStrategy(ipStrategy())
.attributeStrategy(attributeStrategy())
.build();
}

@Bean
public AttributeStrategy attributeStrategy() {
return new SessionAttributeStrategy();
public IPinfo ipInfo() {
return new IPinfo.Builder().setToken(accessToken).build();
}

@Bean
public InterceptorStrategy interceptorStrategy() {
return new BotInterceptorStrategy();
}

public String getAccessToken() {
return accessToken;
@Bean
public IPStrategy ipStrategy() {
return new XForwardedForIPStrategy();
}

@Bean
public AttributeStrategy attributeStrategy() {
return new SessionAttributeStrategy();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ public class SecurityConfig {

private final OpenApiPatternsProperties OpenApiPatternsProperties;

private final IPInfoConfig ipInfoConfig;

@Value("${resource.file.url}")
String fileURL;

Expand All @@ -79,7 +81,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.authorizeRequests(this::configureRequests)
.authenticationProvider(authenticationConfig.authenticationProvider())
.addFilterBefore(
new IpAuthenticationFilter(),
new IpAuthenticationFilter(ipInfoConfig),
UsernamePasswordAuthenticationFilter.class
)
.addFilterBefore(
Expand Down
5 changes: 0 additions & 5 deletions src/main/java/page/clab/api/global/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package page.clab.api.global.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.ipinfo.spring.IPinfoSpring;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -30,9 +29,6 @@ public class WebConfig implements WebMvcConfigurer {
@Autowired
private ApiLoggingInterceptor apiLoggingInterceptor;

@Autowired
private IPinfoSpring ipinfoSpring;

@Value("${resource.file.path}")
private String filePath;

Expand Down Expand Up @@ -67,7 +63,6 @@ public MappingJackson2HttpMessageConverter jsonEscapeConverter() {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(ipinfoSpring);
registry.addInterceptor(apiLoggingInterceptor);
}

Expand Down
13 changes: 10 additions & 3 deletions src/main/java/page/clab/api/global/util/HttpReqResUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Set;

public class HttpReqResUtil {

private static final Set<String> localIpSet = Set.of("0:0:0:0:0:0:0:1", "127.0.0.1", "192.168.0.1");

private static final String[] IP_HEADER_CANDIDATES = {
"X-Forwarded-For",
"Proxy-Client-IP",
Expand All @@ -27,12 +31,15 @@ public static String getClientIpAddressIfServletRequestExist() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
for (String header : IP_HEADER_CANDIDATES) {
String ipList = request.getHeader(header);
if (ipList != null && ipList.length() != 0 && !"unknown".equalsIgnoreCase(ipList)) {
String ip = ipList.split(",")[0];
return ip;
if (ipList != null && !ipList.isEmpty() && !"unknown".equalsIgnoreCase(ipList)) {
return ipList.split(",")[0];
}
}
return request.getRemoteAddr();
}

public static boolean isLocalRequest(String ipAddress) {
return localIpSet.contains(ipAddress);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,39 @@
import io.ipinfo.spring.strategies.attribute.AttributeStrategy;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import page.clab.api.global.common.dto.IpInfoResponse;
import page.clab.api.global.common.dto.IPInfoResponse;
import page.clab.api.global.config.IPInfoConfig;

@Slf4j
@Component
public class IpInfoUtil {
public class IPInfoUtil {

private static final RestClient restClient = RestClient.create();

private static final String IPINFO_BASE_URL = "https://ipinfo.io/";
private static RestClient restClient;

private static String accessToken;

private static AttributeStrategy attributeStrategy;

public IpInfoUtil(IPInfoConfig ipInfoConfig) {
public IPInfoUtil(IPInfoConfig ipInfoConfig) {
restClient = ipInfoConfig.getRestClient();
accessToken = ipInfoConfig.getAccessToken();
IpInfoUtil.attributeStrategy = ipInfoConfig.attributeStrategy();
IPInfoUtil.attributeStrategy = ipInfoConfig.attributeStrategy();
}

public static IpInfoResponse getIpInfo(String ipAddress) {
public static IPInfoResponse getIpInfo(String ipAddress) {
return restClient.get()
.uri(IPINFO_BASE_URL + ipAddress + "?token=" + accessToken)
.uri(ipAddress)
.header("Authorization", "Bearer " + accessToken)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, ((request, response) -> {
log.warn("4xx error occurred while getting ip info. Status code: {}", response.getStatusCode());
}))
.body(IpInfoResponse.class);
.body(IPInfoResponse.class);
}

public static IPResponse getIpInfo(HttpServletRequest request) {
Expand Down

0 comments on commit e4d6d53

Please sign in to comment.