搜索

java获取客户端ip地址

发表于 2026-10-17 06:15:17 来源:烟消火灭网

拿到`request.getRemoteAddr()`返回`127.0.0.1`的客户时候,我就知道这事儿没完。端ip地明明请求从外网进来,客户经过Nginx转发,端ip地怎么到Servlet手里就成了本地回环地址?客户答案很简单:中间有代理,TCP连接是端ip地代理服务器建的,`getRemoteAddr()`拿到的客户自然是代理的IP。

先搞清楚一个前提。端ip地HTTP是客户应用层协议,它本身不携带"客户端IP"这种字段。端ip地服务器能看到的客户只有TCP连接对端的地址。没有代理的端ip地情况下,`request.getRemoteAddr()`返回的客户就是真实客户端IP,这个没什么好说的端ip地。问题出在代理链路上——反向代理、客户负载均衡、CDN,任何一层都会让这个值失真。

代理服务器转发请求时,通常会在HTTP头里追加一些字段来记录原始信息。常见的几个:

  • X-Forwarded-For:最普遍的一个。格式是逗号分隔的IP列表,每经过一层代理就追加一个。最左边是原始客户端,最右边是最后一个代理。
  • X-Real-IP:Nginx常用,通常只存一个IP,就是直接上游的地址。
  • Proxy-Client-IP / WL-Proxy-Client-IP:WebLogic和Apache的一些历史遗留头。
  • HTTP_CLIENT_IP:某些代理会设置。
  • X-Forwarded-Host、X-Forwarded-Proto:分别记录原始Host和协议,和IP无关但经常一起出现。

所以取真实IP的代码大概长这样:

public static String getClientIp(HttpServletRequest request) {  String ip = request.getHeader("X-Forwarded-For"); if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {  ip = request.getHeader("X-Real-IP"); } if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {  ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {  ip = request.getRemoteAddr(); } // X-Forwarded-For 可能有多个IP,取第一个 if (ip != null && ip.contains(",")) {  ip = ip.split(",")[0].trim(); } return ip; } 

这段代码网上一搜一大把,但直接用是有坑的。

第一个坑:X-Forwarded-For可以被伪造。客户端自己就能设这个头。如果你的服务直接暴露在公网,没有可信代理在前面,攻击者随便发一个X-Forwarded-For: 1.2.3.4,你的代码就信了。正确做法是:只在请求确实来自可信代理时才读这些头。判断来源可以用`request.getRemoteAddr()`对比可信代理的IP白名单。

第二个坑:多层代理时取哪个IP。假设链路是:客户端 → CDN → Nginx → Tomcat。XFF可能是真实IP, CDN的IP。你取第一个拿到真实IP,没问题。但如果攻击者在客户端伪造了XFF,链路变成:客户端(伪造XFF: 假IP) → CDN → Nginx → Tomcat,XFF就变成假IP, 真实IP, CDN的IP。你取第一个拿到的是假IP。更稳妥的方式是从右往左数,跳过已知的可信代理,第一个非可信IP才是真的。不过实现起来麻烦,很多团队直接信任最左边的值,配合WAF和限流也能扛住大部分场景。

第三个坑:IPv6。`getRemoteAddr()`可能返回`0:0:0:0:0:0:0:1`,这就是IPv6的localhost。如果你的代码里用`equals("127.0.0.1")`判断本地请求,IPv6环境下会漏掉。用`InetAddress.getByName(ip).isLoopbackAddress()`更靠谱。

还有个细节:Nginx配置里要确保传了这些头。典型的配置是:

location / {  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_pass http://backend; } 

`$proxy_add_x_forwarded_for`会把已有的XFF加上`$remote_addr`追加到后面。如果Nginx前面还有一层,比如CDN,那`$remote_addr`拿到的是CDN的IP,不是客户端的。这种情况需要在Nginx层面再用`real_ip_module`做一次还原。

实际项目里我见过更离谱的:有人把`getRemoteAddr()`的结果直接存数据库当用户标识,上线后发现所有用户IP都是同一个——因为前面挂了F5负载均衡,没配XFF透传。改配置比改代码快,但前提是你得知道问题出在哪一层。

排查这类问题有个笨办法但很有效:写个测试接口,把`getRemoteAddr()`和所有相关请求头全部打印出来,然后用curl从不同链路打一遍,对比结果。比看文档猜快得多。

随机为您推荐
友情链接
版权声明:本站资源均来自互联网,如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

Copyright © 2016 Powered by java获取客户端ip地址,烟消火灭网   sitemap

回顶部