在Java开发中,获取客户端IP地址是一个常见的需求,尤其在用户行为分析、访问统计、安全防护等场景中,由于网络环境的复杂性(如代理服务器、负载均衡器等),直接获取客户端真实IP地址并非易事,本文将系统介绍Java获取客户端IP的方法、注意事项及最佳实践。

HTTP请求头中的IP地址
客户端IP地址通常包含在HTTP请求头中,常见的字段有X-Forwarded-For、Proxy-Client-IP、WL-Proxy-Client-IP、HTTP_CLIENT_IP、HTTP_X_FORWARDED_FOR等,这些字段的意义如下:
X-Forwarded-For(XFF):标准HTTP请求头,用于表示客户端IP地址,以及后续的代理服务器IP地址,格式为client_ip, proxy1_ip, proxy2_ip,...,第一个IP为客户端真实IP。Proxy-Client-IP:Apache服务器添加的请求头,标识客户端IP。WL-Proxy-Client-IP:WebLogic服务器添加的请求头。HTTP_CLIENT_IP:有些代理服务器会使用此字段。HTTP_X_FORWARDED_FOR:与X-Forwarded-For等效,非标准但常用。
需要注意的是,这些请求头可能被伪造,因此在安全敏感场景下需谨慎使用。
直接获取客户端IP的常见方法
在Java中,可通过Servlet API的HttpServletRequest对象获取客户端IP,以下是基础实现:
public String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "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.getRemoteAddr(); // 直接连接客户端的IP
}
return ip;
}
request.getRemoteAddr()
该方法返回客户端的IP地址,但存在局限性:
- 如果客户端通过代理服务器访问,
getRemoteAddr()返回的是代理服务器的IP,而非客户端真实IP。 - 在集群环境中,若请求经过负载均衡器,同样会获取到负载均衡器的IP。
优先级判断请求头
通过遍历上述请求头,优先获取X-Forwarded-For等字段,可提高获取真实IP的概率,但需注意:

X-Forwarded-For可能包含多个IP,需取第一个非未知IP。- 若请求未经过代理,部分请求头可能为空。
处理代理服务器场景
在真实生产环境中,客户端请求通常经过多层代理(如Nginx、CDN、负载均衡器等),此时需结合X-Forwarded-For和getRemoteAddr()综合判断。
Nginx反向代理配置
若使用Nginx作为反向代理,需在Nginx配置中添加X-Forwarded-For头:
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://backend;
}
X-Forwarded-For的格式为客户端IP, 代理IP1, 代理IP2。
代码优化:提取真实IP
针对多层代理场景,可优化代码逻辑:
public String getRealIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
// 多个IP时,取第一个非内网IP
String[] ips = ip.split(",");
for (String strIp : ips) {
if (!isInternalIp(strIp.trim())) {
return strIp.trim();
}
}
return ips[0].trim();
}
ip = request.getHeader("X-Real-IP");
if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
return request.getRemoteAddr();
}
内网IP过滤
需排除内网IP(如0.0.0/8、16.0.0/12、168.0.0/16等),避免将代理服务器IP误认为客户端IP:

private boolean isInternalIp(String ip) {
if (ip == null || ip.isEmpty()) {
return false;
}
try {
InetAddress address = InetAddress.getByName(ip);
return address.isSiteLocalAddress() || address.isLoopbackAddress();
} catch (UnknownHostException e) {
return false;
}
}
Spring Boot中的IP获取
在Spring Boot项目中,可通过封装工具类简化IP获取逻辑,以下是完整示例:
创建IP工具类
@Component
public class IpUtils {
private static final String UNKNOWN = "unknown";
private static final String[] IP_HEADERS = {
"X-Forwarded-For",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_CLIENT_IP",
"HTTP_X_FORWARDED_FOR"
};
public static String getClientIp(HttpServletRequest request) {
String ip = null;
for (String header : IP_HEADERS) {
ip = request.getHeader(header);
if (ip != null && !ip.isEmpty() && !UNKNOWN.equalsIgnoreCase(ip)) {
break;
}
}
if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 处理多IP情况
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
}
public static boolean isInternalIp(String ip) {
if (ip == null || ip.isEmpty()) {
return false;
}
try {
InetAddress address = InetAddress.getByName(ip);
return address.isSiteLocalAddress() || address.isLoopbackAddress();
} catch (UnknownHostException e) {
return false;
}
}
}
使用示例
@RestController
public class IpController {
@GetMapping("/ip")
public String getIp(HttpServletRequest request) {
return "Client IP: " + IpUtils.getClientIp(request);
}
}
注意事项与最佳实践
- 安全性:
X-Forwarded-For等请求头可被伪造,需结合业务场景判断可信度,在金融、支付等敏感场景,建议结合客户端证书、Token等方式验证身份。 - 性能优化:避免在每次请求中重复解析IP,可通过过滤器(Filter)或拦截器(Interceptor)缓存IP地址。
- 日志记录:在日志中记录完整请求链路(如
X-Forwarded-For全链路IP),便于问题排查。 - IPv6支持:当前代码主要针对IPv4,若需支持IPv6,需调整IP校验逻辑(如使用
InetAddress的getAddress()方法)。 - 测试覆盖:需测试直连、单层代理、多层代理、内网穿透等多种场景,确保IP获取准确性。
获取客户端IP地址需综合考虑网络架构、代理层级及安全性,基础实现可通过HttpServletRequest的请求头和getRemoteAddr()方法,生产环境中需结合Nginx等代理配置优化逻辑,并过滤内网IP和伪造请求头,Spring Boot项目中可通过工具类封装简化调用,同时需注意性能与安全平衡,通过合理的设计,可准确、高效地获取客户端真实IP地址,为业务系统提供可靠数据支撑。















