본문 바로가기
Back-End

[Java] Client(사용자) IP 가져오기

by 호강하는 지해 2024. 11. 25.
728x90

 

 

문제 상황

일반적인 웹 개발을 할 때에 서버에 접속한 클라이언트의 IP를 가져오는 것은 필수다. 로그인 이력 등의 로그성 데이터에 있어서 클라이언트 IP는 가장 중요한 정보이기 때문이다.

스프링을 사용하면 HttpServletRequest 객체의 getRemoteAddr() 메서드를 통해 손쉽게 IP를 조회할 수 있는데,

사실 이 방법은 프록시나 로드 밸런서 또는 웹서버를 사용하여 들어오는 사용자의 클라이언트 IP는 조회할 수 없었을 뿐더러, 조회되는 IP도 사용자의 실제 IP 주소가 아니라는 점을 알게되었다. 

 

 

해결 방안

그래서 이를 해결하기 위해 HTTP Header를 확인하여 처리하는 방식을 사용했는데 이에 대해 자세히 설명해보겠다 !

 

 

 

@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {

    UserLotto userLotto = (UserLotto) body;
    String ip = request.getRemoteAddr();
    userLotto.setCrtIp(ip);  // IP 주소 설정
 
	return body;
}

이건 내가 원래 짰었던 코드의 일부다. 위 코드처럼 사실 아주 간단하게 IP를 조회해올 수 있다.

 

그러나 위에서 말했다시피 프록시로드 밸런서를 사용하여 들어오는 사용자의 클라이언트 IP는 조회할 수 없었을 뿐더러, 조회되는 IP도 사용자의 실제 IP 주소가 아닌 프록시의 IP와 로드 밸런서의 IP다.

또한 클라이언트의 요청이 웹 서버(예: Apache)를 통해 WAS로 전달되는 경우엔 웹 서버의 IP가 조회된다.

 

 

@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {

    String clientIp = null;
    UserLotto userLotto = (UserLotto) body;

    List<String> headerList = List.of(
            "X-Forwarded-For", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR",
            "HTTP_X_FORWARDED", "HTTP_FORWARDED_FOR", "HTTP_FORWARDED",
            "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_VIA", "IPV6_ADR"
    );

    for (String header : headerList) {
        clientIp = request.getHeader(header);
        if (StringUtils.hasText(clientIp) && !clientIp.equalsIgnoreCase("unknown")) {
            userLotto.setCrtIp(clientIp);
            break;
        }
    }

    if (!StringUtils.hasText(clientIp)) {
        // 그렇지 않은 경우에는 request.getRemoteAddr()를 통해 기본적인 IP 주소를 얻기
        userLotto.setCrtIp(request.getRemoteAddr());
    }

    return body;
}

위 코드는 내가 최종적으로 수정한 코드다.

 

List에 헤더 리스트를 정의해준 후, for 문을 통해 각 헤더를 조회하며 request.getHeader(header)로 해당 헤더의 값을 가져와서 유효한 IP를 찾으면 값을 넣어주고,

만약 헤더를 모두 확인했는데도 유효한 IP를 찾지 못한 경우, request.getRemoteAddr() 메서드를 사용해 기본적인 IP를 가져와 값을 넣어준다.

 

 

여기서 잠깐 ! 왜 header를 조회해야할까?

클라이언트의 IP 주소를 정확하게 얻기 위해 헤더를 조회해야 하는 이유는
프록시/로드밸런서/웹서버는 클라이언트와 애플리케이션 서버 간의 중계 역할을 하기 때문에, 클라이언트의 실제 IP를 header에 포함시켜서 WAS에 전달하기 때문에, 클라이언트가 실제로 접속한 서버의 IP는 HTTP 요청 헤더에서 조회해야한다.

 

 

+ IP를 공부하다보니 생각보다 어렵고 공부할 것이 많아서 글로 정리할 예정 ! (URL 추가 예정!)

 

 

참조

https://inpa.tistory.com/entry/WEB-%F0%9F%8C%90-IP-%EA%B8%B0%EC%B4%88-%EC%82%AC%EC%84%A4IP-%EA%B3%B5%EC%9D%B8IP-NAT-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A7%90-%EC%89%BD%EA%B2%8C-%EC%A0%95%EB%A6%AC


https://adjh54.tistory.com/443#google_vignette

 

 

 

728x90

'Back-End' 카테고리의 다른 글

[JAVA] DTO들의 부모 DTO 인터페이스 만들기  (2) 2024.11.12