问题来源
获取用户客户端IP地址是一个很简单的需求。用户打开网页,发送一个HTTP请求,后台从中提取出IP地址。
但是,在使用微信打开网页时,发现获取到的IP地址和在浏览器打开所得到的IP地址不一样。用不同的浏览器,测试多次,都得到一个这样的结论。
中间一定是出了什么问题。
Solution
其实这是使用了代理的问题,后台直接从HTTP头中获取到的host是最后一次经过的代理的地址。
幸运的是,http包在转发过程中,保留下了代理路径信息,x-forwarded-for字段,一个典型的示例如下:
x-forwarded-for: 192.168.1.110, 192.168.1.120, 192.168.1.130, 192.168.1.100
如何确定客户端的IP呢?
取x-forwarded-for中第一个非unknown的有效IP字符串。
一个小小的获取客户端IP地址,做起来也没那么简单!
扩展:NAT子网地址
扩展一下,我们所说的客户端的IP地址,其实多数情形下,是客户端所在子网的出口地址。由于IPv4地址耗尽的问题,现在大家用的网络多数是NAT扩展出来的,能拿到的自然就是客户端的出口地址。
进一步的,如果想区分同一子网下的不同设备,该怎么办?IP地址是不够的,大家都一样。依据HTTP请求,我们唯一能获取的信息就是User-Agent了。各个浏览器实现的User-Agent格式都不太一样,但大体都会包括一些有用信息,例如浏览器标识、操作系统标识、浏览器语言、渲染引擎、版本信息等。如果是移动端浏览器,一般还有设备信息,比较典型的如下:
# iPhone,微信
Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B466 MicroMessenger/6.2.6 NetType/3G+ Language/zh_CN
# 安卓,微信
Mozilla/5.0 (Linux; U; Android 4.4.2; zh-cn; GT-I9500 Build/KOT49H) AppleWebKit/533.1 (KHTML, like Gecko)Version/4.0 MQQBrowser/5.4 TBS/025489 Mobile Safari/533.1 MicroMessenger/6.3.15.49_r8aff805.760 NetType/WIFI Language/zh_CN
# 安卓,小米
Mozilla/5.0 (Linux; U; Android 5.0.2; zh-cn; MI 2S Build/LRX22G) AppleWebKit/533.1 (KHTML, like Gecko)Version/4.0 MQQBrowser/5.4 TBS/025489 Mobile Safari/533.1 V1_AND_SQ_6.2.3_336_YYB_D QQ/6.2.3.2700 NetType/WIFI WebP/0.3.0 Pixel/720
现在的移动端浏览器的User-Agent信息更丰富,设备、网络、语言都可以获取到。由于浏览器的多样性,User-Agent信息是比较繁杂的。这大概是我们透过HTTP请求能够最大程度识别到的用户了。