一、场景
产品想要增加一个操作日志的模块,重点记录增删改的操作ip。
二、问题描述
这块业务其实需要按【是否代理】来分逻辑。首次开发时,并未考虑 测试生产环境有统一代理 ,导致传统的获取ip方式取到了容器网关ip。——显然这样是无法满足产品想要区分操作ip。
三、调研过程
解决问题首先要明确一些基础内容。参考如下:
3.1 什么是req.ip?
当 trust proxy 为false时,req.ip的取值与req.connection.remoteAddress一致;
当 trust proxy 为true时,req.ip的取值与X-Forwarded-For的首个IP一致。
而 trust proxy 默认为false,因此 req.ip 默认为remoteAddress。
注意:这里引出两个概念:remoteAddress和X-Forwarded-For。
3.2 什么是remoteAddress?
如果使用了代理,如nginx,客户端发出的请求会先通过代理,由代理转发给服务器。remoteAddress就是代理ip。
如果不使用代理,客户端发出的请求会直接给服务器,remoteAddress就是真实ip。
3.3 什么是X-Forwarded-For?
同样的道理,假设真实ip为Proxy0:
如果使用了代理,如Proxy1,那么客户端发出的请求是以Proxy0,Proxy1的过程到达服务器,X-Forwarded-For就用英文逗号+空格记录代理过程,参考上图。
如果不使用代理,客户端发出的HTTP请求默认没有X-Forwarded-For头部,又因为不经过代理服务器,所以到达目标服务器时,依然没有X-Forwarded-For。
3.4 什么是X-Real-Ip?
X-Real-Ip的使用环境多为nginx配置,在配置中通过Set Header方法把转发前的Proxy0,也就是remoteAddress值赋给X-Real-Ip头部,避免在代理过程中丢失真实ip。
四、实践
综上内容,进行调试。我们依次在debug环境、production环境进行验证:
4.1 客户端ip不经过代理,直接访问 ✔
结论:能获取真实ip。
客户端ip:127.xx.xx.49 ===> 服务器ip:172.xx.xx.20
4.2 客户端ip不经过代理,访问容器 ✖
结论:不能获取真实ip。
客户端ip:127.xx.xx.49 ===> 容器ip:172.xx.xx.246(服务器ip:172.xx.xx.250)
4.3 客户端ip经过1次代理,访问容器 ✔
结论:能获取真实ip。
客户端ip:127.xx.xx.49 ===> 代理ip:172.xx.xx.20 ===> 容器ip:172.xx.xx.246(服务器ip:172.xx.xx.250)
五、总结
以上仅为我个人的粗浅理解,实际还有伪造请求头的场景存在。
包括这也只是web端在电脑上的ip记录,另外还有外网环境下移动端的ip记录等。
路过还请指正。