📌 1. Client IP 추출 방법
📖 getClientIp 예제
public String getClientIp(HttpServletRequest request){
String ip = request.getHeader("X-Forwarded-For");
//1️⃣여러 IP가 있을 경우 첫 번째 IP만 사용
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
//2️⃣기타 프록시 관련 헤더 확인
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-RealIP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("REMOTE_ADDR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
//3️⃣IPv6 loopback 또는 localhost 처리
if ("0:0:0:0:0:0:0:1".equals(ip) || "127.0.0.1".equals(ip)) {
InetAddress address;
//4️⃣예외 처리
try {
address = InetAddress.getLocalHost();
ip = address.getHostAddress(); //address.getHostName()
} catch (UnknownHostException e) {
e.printStackTrace();
ip = "127.0.0.1";
}
}
//5️⃣로깅
logger.info("Client IP: {}", ip);
return ip;
}
1️⃣X-Forwarded-For 안에 여러 IP가 들어올 때 분리해서 처리
2️⃣헤더 순서대로 진짜 IP를 찾음.
3️⃣로컬호스트일 경우에는 getLocalHost()를 써서 IP + 호스트명을 반환
4️⃣UnknownHostException 처리
5️⃣Ip 로깅
📌 X-Forwarded-For란?
클라이언트가 HTTP 요청을 보낼 때, 프록시 서버(예: Nginx, AWS ALB 등)가
클라이언트 IP를 보존하기 위해 이 헤더에 IP를 붙여서 전달함.
📌 여러 IP가 들어오는 이유?
요청이 여러 프록시를 통과할수록, 프록시마다 X-Forwarded-For에 IP를 추가하기 때문.
📖 예시:
X-Forwarded-For: 123.45.67.89, 10.0.0.1, 172.31.255.3
✔️123.45.67.89: 진짜 클라이언트 IP (브라우저가 쏜 IP)
✔️10.0.0.1: 첫 번째 프록시 서버
✔️172.31.255.3: 두 번째 프록시 서버
📌 2. 다중 디바이스 환경에서 IP 기반 로그인
✅ 2-1. 최근 로그인된 IP 리스트 관리 (화이트리스트 방식)
- USER_ID 기준으로 최근 사용한 IP 최대 3개까지 저장
- 로그인 시 등록된 IP 중 하나면 허용
- 새로운 IP에서 접근 시
→ 추가 인증 요청 (예: 2차 인증, 이메일 알림 등)
✅ 2-2. Access Token + IP 매핑 방식 (디바이스 단위로)
- ACCESS_TOKEN과 USER_ID를 기준으로 IP 하나 매핑됨
- 로그인 시 토큰이 유효하더라도, 요청한 IP가 해당 토큰에 저장된 IP와 다르면 재로그인 유도
✅ 2-3. 다비이스 타입 + IP 매핑
- DB컬럼에 디바이스 타입 추가(예: MOBILE_YN)
- User-Agent에서 디바이스 정보를 추출
- 최신순 정렬, limit 1해서 디바이스 타입별 최근 1개 IP가져옴.
🔍 User-Agent란?
- User-Agent는 클라이언트(브라우저나 앱)가 서버에 요청을 보낼 때 자신의 정보를 담아서 보내는 HTTP 헤더.
- User-Agent가 담고 있는 정보: 브라우저(Chrome, Edge 등), 운영체제(Windows, Android, iOS 등), 디바이스(모바일, 태블릿, PC 등), 기타(웹 버전, 웹킷 등)
📖 디바이스 타입 구분 예제
public static String isMobile(HttpServletRequest req) {
String userAgent = req.getHeader("User-Agent").toUpperCase();
String[] mobileKeywords = {
"ANDROID", "IPHONE", "IPAD", "IPOD", "MOBILE", "MOBI"
};
logger.info("userAgent: {}", userAgent);
if (userAgent != null) {
userAgent = userAgent.toUpperCase();
for (String keyword : mobileKeywords) {
if (userAgent.contains(keyword)) {
return "Y";
}
}
}
return "N";
}
'Backend > JAVA' 카테고리의 다른 글
| Streaming이란? (0) | 2025.12.18 |
|---|---|
| json파일 읽기 (0) | 2025.12.18 |
| 대량 DB INSERT 최적화 (0) | 2025.11.29 |
| 파일 업로드, 다운로드 (MultipartFile) (0) | 2025.11.08 |
| JUnit 단위 테스트 적용, JaCoCo 커버리지 측정 (0) | 2025.08.27 |