当前位置:首页 > 问答 > 正文

Redis用起来定位IP难题没那么难,教你快速搞定ip获取技巧

前段时间,我们项目里遇到一个挺让人头疼的问题:用户登录的时候,我们需要根据他的IP地址来判断他大概在哪个区域,然后给他展示一些本地化的内容,比如当地的天气或者附近的优惠活动,听起来很简单对吧?但真做起来,问题一大堆。

最开始的方案是,我们在用户每次登录请求到达后端服务器的时候,直接从HTTP请求头里把IP地址取出来,听起来天经地义,对吧?但很快就发现不对劲,我们收到的IP地址,十个里有八个都是同一个,而且这个IP还不是我们公司的公网IP,查了一下发现是某个云服务商的IP,这下可傻眼了,用户的真实IP去哪儿了?

我们赶紧查资料,发现这个问题太常见了,根据网上很多技术博客,比如像CSDN、博客园上一些有经验的开发者分享,现在的网络环境很复杂,用户的请求很少是直接到达我们服务器的,中间可能经过了层层“关卡”。(来源:综合自CSDN、博客园等多篇关于“Nginx获取真实IP”的技术文章)

第一关:代理服务器和负载均衡。 很多公司为了安全和性能,会在服务器前面部署像Nginx这样的反向代理服务器或者F5这样的负载均衡设备,用户的请求先到达这些代理,代理再转发给我们的应用服务器,这样一来,我们应用服务器看到的客户端IP就成了代理服务器的内网IP,而不是用户的真实IP了,这就好比你去寄信,不是直接送到收件人手里,而是先送到小区门卫,门卫再转交,那收件人看到的寄件人地址,可能就变成了门卫室的地址。

第二关:CDN。 如果网站用了CDN(内容分发网络),情况就更复杂了,用户可能先访问到离他最近的CDN节点,CDN节点再去源服务器拿数据,这时候,源服务器看到的IP就是CDN节点的IP。

Redis用起来定位IP难题没那么难,教你快速搞定ip获取技巧

那怎么办呢?难道就没办法拿到真实IP了吗?当然不是,那些技术文章里提到了一个关键的机制:X-Forwarded-For 这个HTTP头。(来源:RFC 7239标准及多个开发者社区讨论)

这个头部的原理很简单,但非常巧妙,当请求经过第一个代理服务器时,代理服务器会把客户端的真实IP地址记录在X-Forwarded-For头里,如果请求继续经过第二个、第三个代理,后面的代理会依次把前一个代理的IP追加到这个头部的末尾,用逗号隔开,X-Forwarded-For的值可能长这样:"用户真实IP, 代理服务器1的IP, 代理服务器2的IP"

这样一来,我们的应用服务器虽然看到的直接连接IP是最后一个代理的IP,但可以通过读取X-Forwarded-For这个头,然后取第一个IP,来获得最原始的用户真实IP,这就像是一封转交了好几次的信,虽然最后是张三交给你的,但信封上按顺序写着“李四转王五转张三”,你一看就知道最初是李四寄出的。

Redis用起来定位IP难题没那么难,教你快速搞定ip获取技巧

这里又有一个坑:这个头是可以被伪造的,如果用户恶意地在请求里自己加一个X-Forwarded-For头,里面写个假的IP,而我们的代理服务器只是简单地追加,那我们取到的第一个IP就是用户伪造的,这就有安全风险。

更可靠的做法是,我们信任最外层的代理(比如我们自己的Nginx),让它来设置这个头,常见的配置是,Nginx会使用$remote_addr这个变量(它代表直接与Nginx通信的客户端IP,对于最外层代理来说就是用户真实IP)来覆盖或者初始化X-Forwarded-For头,防止用户伪造,我们的后端应用再从X-Forwarded-For里取第一个IP,这样就安全多了。

除了X-Forwarded-For,有时还会看到X-Real-IP这样的头,这个通常是由最靠近用户的代理设置的,直接就是用户的真实IP,不包含中间经过的IP列表,但它不是一个标准,取决于你的代理服务器怎么配置。

总结一下获取用户真实IP的技巧,关键几步是:

  1. 理清网络架构:先搞清楚用户的请求到底经过了哪些节点,比如CDN、负载均衡、反向代理等,这一步最重要,不然就是瞎猜。
  2. 配置可信代理:在最外层的网关节点(如Nginx)上,正确配置HTTP头,通常是设置或覆盖X-Forwarded-For,确保其第一个IP是可信的用户真实IP。
  3. 后端正确解析:在后端代码(如Java、Python、PHP)中,编写一个通用的方法,不是简单地取直接连接的IP,而是优先从X-Forwarded-For这样的头里解析,并取第一个IP,同时要做好校验,比如判断这个IP是否是合法的公网IP格式。

解决了IP获取的问题,我们项目里的区域判断功能总算能正常工作了,这个过程让我明白,很多看似简单的问题,背后可能藏着复杂的网络知识,但只要理解了原理,一层层去排查,再难的“IP定位”问题也能找到突破口,希望我们的这次经历,能帮你以后遇到类似问题时快速搞定。