在生產環境中,應用常部署在 Nginx、Apache 等反向代理或負載均衡器後端,直接使用 request.getRemoteAddr() 只能獲取到代理服務器的 IP。本文提供一個健壯的工具方法,能夠穿透多層代理獲取用戶真實 IP。
/**
* 獲取客戶端真實 IP(支援公網、局域網、多級代理)
* @param request HttpServletRequest 對象
* @return 客戶端 IP 地址
* @throws Exception 請求對象為空時拋出
*/
public static String getIpAddr(HttpServletRequest request) throws Exception {
if (request == null) {
throw new Exception("HttpServletRequest 對象不能為空");
}
String ip = request.getHeader("x-forwarded-for");
if (isInvalidIp(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (isInvalidIp(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (isInvalidIp(ip)) {
ip = request.getRemoteAddr();
}
// 多級代理情況下,取第一個非 unknown 的 IP
if (ip != null && ip.contains(",")) {
String[] ips = ip.split("\\s*,\\s*");
for (String candidate : ips) {
if (!isInvalidIp(candidate)) {
ip = candidate;
break;
}
}
}
return ip;
}
private static boolean isInvalidIp(String ip) {
return ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip);
}
| Header | 來源 | 適用場景 |
|---|---|---|
| x-forwarded-for | 標準代理頭,格式:client, proxy1, proxy2 | Nginx、Apache、CDN、AWS ALB 等 |
| Proxy-Client-IP | Apache HTTP Client / Squid 代理 | 內部服務調用鏈 |
| WL-Proxy-Client-IP | WebLogic 代理 | WebLogic 集群環境 |
| getRemoteAddr() | Servlet 容器直接連接 | 無代理、直連環境 |
x-forwarded-for 可被客戶端偽造,關鍵業務(如風控、限流)應配合代理服務器配置,只信任可信代理添加的頭::ffff:192.168.1.1),可增加處理邏輯可封裝為 RequestContextHolder 獲取請求的工具類,或通過 HandlerInterceptor 統一放入 ThreadLocal 供業務使用。