-
Spring RequestContextHolder으로 Client IP 주소 가져오기Spring Framework 2023. 7. 22. 00:01
RequestContextHolder란?
Spring 2.x부터 제공되던 기능으로 Spring 전역으로 Request에 대한 정보를 가져오고자 할 때 사용하는 유틸성 클래스입니다.
주로 Controller가 아닌, Service에서 Request 객체를 참고하려 할 때 사용합니다.
private static final boolean jsfPresent = ClassUtils.isPresent("jakarta.faces.context.FacesContext", RequestContextHolder.class.getClassLoader()); private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal<>("Request attributes"); private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal<>("Request context");
Servlet이 생성될 때 클래스가 초기화되며 내부 필드로는 ThreadLocal으로 값들을 가지고 있습니다.
주의할 점으로는 new Thread 혹은 Executor를 사용한 ThreadPool에서의 참조 등에서는 DispatcherServlet 범위에서 벗어나서 새로운 스레드가 생성되기 때문에 RequestContextHolder에서 값을 꺼내올 수 없습니다.
CurrentRequestAttributes 메서드
쓰레드 바운더리 내에 존재하는 RequestAttributes 클래스를 가져옵니다.
RequestAttributes 인터페이스
global session이라는 optional 개념을 통해 모든 종류의 request/session 메커니즘을 구현합니다.
특히 servlet requests에 대해 구현할 수 있습니다.
ServletRequestAttributes 클래스
RequestAttribute의 구현체로써 session, global session을 구분하지 않고 servlet request 객체에 접근할 수 있습니다.
getRequest로 HttpServletRequest를 꺼내옵니다.
HttpServletRequest 클래스
/** * Returns the value of the specified request header as a <code>String</code>. If the request did not include a * header of the specified name, this method returns <code>null</code>. If there are multiple headers with the same * name, this method returns the first head in the request. The header name is case insensitive. You can use this * method with any request header. * * @param name a <code>String</code> specifying the header name * * @return a <code>String</code> containing the value of the requested header, or <code>null</code> if the request * does not have a header of that name */ String getHeader(String name);
HTTP servlet 정보를 제공하기 위해 ServletRequest 인터페이스를 extends 하여 구현합니다.
getHeader 메서드를 통하여 지정된 요청 헤더의 값을 문자열로 반환하고 존재하지 않는 경우 null을 반환합니다.
예시 코드
private fun getIpAddr(): String? { val req = (RequestContextHolder.currentRequestAttributes() as ServletRequestAttributes).request var ip = req.getHeader("X-FORWARDED-FOR") if (ip == null) ip = req.remoteAddr return ip }
1. RequestContextHolder에서 현재 쓰레드의 RequestAttribtes를 꺼내온다.
2. 구현체인 ServletRequestAttributes로 변환한다.
3. getRequest를 통해 HttpServletRequest를 꺼내온다.
4. X-FORWARDED_FOR을 활용하여 클라이언트의 IP주소를 꺼내온다.
5. 만약 null이라면 HttpServletRequest.remoteAdr을 얻어와서 반환한다.
X-Forwarded-For란?
- 보통 클라이언트는 forward 또는 reverse proxy를 통해 연결되므로 서버는 최종 proxy의 IP만 알게 되어 쓸모 없어지는 경우가 많습니다.
- 이때 request header인 X-Forwarded-For를 활용하여 de-facto(사실상 표준)으로 클라이언트의 원본 IP 주소를 구별할 수 있습니다.
getRemoteAdr 메서드
Returns the Internet Protocol (IP) address of the client or last proxy that sent the request. For HTTP servlets, same as the value of the CGI variable REMOTE_ADDR. Returns: a String containing the IP address of the client that sent the request String getRemoteAddr();
요청을 보낸 클라이언트 또는 마지막 프록시의 IP 주소를 반환합니다.
HTTP Servlet의 경우에는 CGI변수 REMOVE_ADDR 값과 동일합니다.
참고자료
https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/X-Forwarded-For
https://gompangs.tistory.com/entry/Spring-RequestContextHolder
'Spring Framework' 카테고리의 다른 글
Spring Boot Multiple Port 사용 (멀티 커넥트, 다중 포트) (0) 2023.07.31 Spring Cloud Data Flow란? (0) 2023.07.25 Spring Local Cache란? (0) 2023.07.19 Kotlin + Spring Boot 3 Spring Data envers 적용 (0) 2023.07.18 log4j, logback, log4j2 비교 (0) 2023.07.16