springboot中如何获取真实ip地址
需求是用户在登录时,密码输错了5次以上要将这个ip禁用,所以如题,我需要在登录接口 代码中获得这次请求的原始ip地址。禁用ip的逻辑已经写好了,目前痛点是获取ip地址,也正是这块代码让我被领导叼了。
既然项目一般都是走nginx和网关代理,那肯定得百度了,于是百度的代码如下:
/**
* 获取ip地址
*
* @param request
* @return
*/
public String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
if (ip != null && !"".equals(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
ip = request.getHeader("X-Forwarded-For");
if (ip != null && !"".equals(ip) && !"unknown".equalsIgnoreCase(ip)) {
int index = ip.indexOf(',');
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
} else {
return request.getRemoteAddr();
}
}
然后我模拟2个用户测了一下(测试结果为化名):
- 公司电脑本地启服务,注册到公司开发环境的nacos上,通过网关调用此登录接口
- 用另一台电脑连公司vpn,其余步骤如上。
request.getHeader("X-Real-IP") 获取的ip地址始终固定是:115.1.2.3 request.getHeader("X-Forwarded-For") 获取的结果固定是: 115.1.2.3,10.x.x.x
- 结果是一个字符串,第一个值与
X-Real-IP 值一样。 - 第二个值一直在动态变化
request.getRemoteAddr() 获取的结果固定是: 192.168.1.10
所以我很疑惑,首先看了下公司日志过滤器那块代码也正是用的上面的方法,完全一样。那么说明是一样的。
-
这方法正确吗? 大概看了下这方法逻辑就是先获取请求体的realIp,获取不到才去获取forward啥的ip。 那我这边为什么始终能获取到realIp且始终固定1个值,我已经模拟了2个用户来登录了。 那不是起不到不同ip的效果了吗。 所以我怀疑是不是这个115.1.2.3 是网关还是哪台服务器的ip。 -
为什么X-Forwarded-For的第一个值与realIp一样且第二个值一直变。且第二个值代表什么ip,为什么一直在变。此刻我怀疑是不是就是我2台电脑的无线网动态变化的ip,那么这么看我得取这第二个值了。
带着这些疑惑,真的很菜的我继续百度+咨询公司运维+最终询问领导。
得到了以下结论:
-
首先明确request.getRemoteAddr() 这块不能要。一看就是内网ip,不然直接锁死了。 在jax的告诫"去掉这块代码,让其为空则不禁用,否则内网锁定ip,且很可能是用户微服务集群2台的ip,那就都不能登录了"下,我删掉了这块代码。 且最终咨询了运维之后发现这个固定的192.168.1.10 正是开发环境的K8S地址!那确实不能返回! -
realIp固定的115.1.2.3 问了运维大哥后知道了是真实ip地址!不是内网地址!且是公司的网关地址。 那我明白了,我连公司vpn及我公司电脑其实都是连的公司wifi,所以最终请求发出去都是走的公司网关,此网关不是web项目的网关! 所以最终地址都一致。那这么看满足需求,1个人连的1个wifi,不能连续输错密码5次否则ip禁用,用其他方式连wifi也一样。 -
“X-Forwarded-For”第一个值与其realIo一样应该是我只有1个代理。这边也确实只有Nginx代理了。 所以“X-Forwarded-For”第二个值开始则是代理服务器的地址。 咨询了jax和让运维看了后发现是k8s的一些容器的地址。 只是这个地址为什么一直变jax也很奇怪。我测了下是每次都在变。 所以原来方法是对的,只去获取forward的第一个值!
“X-Forwarded-For”的字面意思是“为谁而转发”,形式上和“Via”差不多,也是每经过一个代理节点就会在字段里追加一个信息。但“Via”追加的是代理主机名(或者域名),而“X-Forwarded-For”追加的是请求方的 IP 地址。所以,在字段里最左边的 IP 地址就是客户端的地址。
“X-Real-IP”是另一种获取客户端真实 IP 的手段,它的作用很简单,就是记录客户端 IP 地址,没有中间的代理信息,相当于是“X-Forwarded-For”的简化版。如果客户端和源服务器之间只有一个代理,那么这两个字段的值就是相同的。
所以最终改造后的代码为:
/**
* 获取真实ip地址,不返回内网地址
*
* @param request
* @return
*/
public String getIpAddr(HttpServletRequest request) {
//目前则是网关ip
String ip = request.getHeader("X-Real-IP");
if (ip != null && !"".equals(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
ip = request.getHeader("X-Forwarded-For");
if (ip != null && !"".equals(ip) && !"unknown".equalsIgnoreCase(ip)) {
int index = ip.indexOf(',');
if (index != -1) {
//只获取第一个值
return ip.substring(0, index);
} else {
return ip;
}
} else {
//取不到真实ip则返回空,不能返回内网地址。
return "";
}
}
总结
实时记录
-
2022年5月7日19:23:54 必须大量写博客啊,既然有明确的标题,那就写吧!
|