-
[Spring Boot] Real Remote(Client) IP Address 얻기개발기록 2022. 10. 11. 16:11반응형728x90
개요
유저의 IP 주소를 얻기위해 구글링 해보면 대부분 아래와 같은 코드를 볼 수 있을 것이다.
이를 봤을 때 if문이 계속 반복된다는 점이 거슬렸고 리팩토링해서 내 코드에 적용해야겠다고 생각했다.
참고: https://jul-liet.tistory.com/202
구글링 시 흔히 보이는 코드
프록시 서버 또는 로드 밸런서 등을 통과하면서 원 IP 주소를 식별하는 Header가 달라질 수 있다.
X-Forwarded-For가 표준 Header로 쓰이고 있지만, 이 외에도 앞서 말한 이유로 인해 Header가 다를 수도 있으니 대표적인 Header들에 몇몇에 대해서 request를 체크할 필요가 있다.
아래 코드들에 보이는 "X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR" 들이 대표적으로 체크해볼 Header 들이다.
cf) 대표적인 Header들을 체크해도 임의로 어딘가(proxy server 등)에서 Header에 대해 임의의 key, value를 설정하여,
원 IP 주소를 못 찾을 수가 있는데, 사수님 말로는 이를 "프록시 헬"이라고 한다고 한다.
※ cf) 프록시 서버(proxy server):
프록시 서버는 클라이언트가 자신을 통해서 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해 주는 컴퓨터 시스템이나 응용 프로그램을 가리킨다. 서버와 클라이언트 사이에 중계기로서 대리로 통신을 수행하는 것을 가리켜 '프록시', 그 중계 기능을 하는 것을 프록시 서버라고 부른다.출처: https://ko.wikipedia.org/wiki/%ED%94%84%EB%A1%9D%EC%8B%9C_%EC%84%9C%EB%B2%84
public static String getClientIP(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); logger.info("X-FORWARDED-FOR : " + ip); if (ip == null) { ip = request.getHeader("Proxy-Client-IP"); logger.info("Proxy-Client-IP : " + ip); } if (ip == null) { ip = request.getHeader("WL-Proxy-Client-IP"); logger.info("WL-Proxy-Client-IP : " + ip); } if (ip == null) { ip = request.getHeader("HTTP_CLIENT_IP"); logger.info("HTTP_CLIENT_IP : " + ip); } if (ip == null) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); logger.info("HTTP_X_FORWARDED_FOR : " + ip); } if (ip == null) { ip = request.getRemoteAddr(); logger.info("getRemoteAddr : "+ip); } logger.info("Result : IP Address : "+ip); return ip; }
리팩토링 및 내 코드에 적용
1. 리팩토링
header 종류들을 배열로 선언한다.
이후 ip address를 담을 변수로 ip를 선언한다.
private String[] headerTypes = {"X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"}; private String ip;
향상된 for문을 통해 header 종류마다 ip 주소가 담겨있는 지 체크한다.
for(String headerType: headerTypes) { ip = request.getHeader(headerType); if(ip != null) break; }
headerTypes 배열에 담긴 header 종류 모두에 ip 주소가 없을 경우,
request.getRemoteAddr()을 통해 remote IP address를 얻어온다.
if (ip == null) ip = request.getRemoteAddr(); log.info("Real Remote(Client) IP Address: " + ip);
2. 내 코드에 적용
requset 로그 찍는 부분에 해당 코드를 적용해서 IP 주소도 로그로 남기도록 코드를 적용했다.
코드를 적용하고 api를 호출하면 IP주소가 0:0:0:0:0:0:0:1로 찍힌다.
이는 IPv6으로 IPv4의 127.0.0.1과 똑같다.
IntelliJ에서 IPv4로 찍히게 하는 방법은 아래 주소를 참고하자.
IPv4 설정 참고: https://gaemi606.tistory.com/entry/IntelliJ-IPv4-%EC%84%A4%EC%A0%95
package com.??.interceptor; import com.fasterxml.jackson.databind.ObjectMapper; import com.??.filter.CachingResponseWrapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.util.ContentCachingRequestWrapper; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; import java.io.StringWriter; @Slf4j @Component public class HttpLogInterceptor implements HandlerInterceptor { private final ObjectMapper objectMapper; // 적용 private String[] headerTypes = {"X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"}; private String ip; public HttpLogInterceptor(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (request instanceof ContentCachingRequestWrapper) { log.info("uri: " + request.getRequestURI()); } // 적용 for(String headerType: headerTypes) { ip = request.getHeader(headerType); if(ip != null) break; } // 적용 if (ip == null) ip = request.getRemoteAddr(); log.info("Real Remote(Client) IP Address: " + ip); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { InputStream tmpIs = ((CachingResponseWrapper) response).getContentInputStream(); StringWriter writer = new StringWriter(); IOUtils.copy(tmpIs, writer, "UTF-8"); String res = writer.toString(); log.info("response - {}", res); } }
728x90'개발기록' 카테고리의 다른 글
[Regex] 정규식 / 정규표현식 (Regular Expression, Regex) (0) 2022.12.23 [Spring Boot] boolean값이 0 또는 false만 return할 때 해결 방법 (0) 2022.11.02 [Spring Boot] request, response 로그 남기기1 - Console: 트러블슈팅 (1) 2022.10.11 [메모장 hosts] hosts파일에서 ip와 도메인 주소 매핑하기 (0) 2022.09.27 [Spring Boot] Mybatis 설정(In application.properties) (0) 2022.09.27