@SuppressWarnings("unchecked") public static ClientIps getClientIpAddr(HttpServletRequest request) { // 获取真实ip String ip = request.getHeader("real-ip"); if (StringUtils.isBlank(ip) || ("unknown".equalsIgnoreCase(ip.trim()))) { ip = request.getHeader("remote-host"); } if (StringUtils.isBlank(ip) || ("unknown".equalsIgnoreCase(ip.trim()))) { ip = request.getRemoteAddr(); } ClientIps clientIps = new ClientIps(); clientIps.setTrueIp(StringUtils.trimToEmpty(ip)); // 获取代理ip ip = request.getHeader("x-forwarded-for"); StringBuilder proxyIps = new StringBuilder(); if (StringUtils.isNotBlank(ip) && (StringUtils.contains(ip, ","))) { String temp = StringUtils.substringBeforeLast(ip, ","); if (StringUtils.isNotBlank(temp)) { proxyIps.append("x-forwarded-for:"); proxyIps.append(temp); proxyIps.append("\n"); } }
获取真实的IP地址
业务系统获取来源IP的正确姿势
下面是一个简单的示意图,简单地把整个访问链路划分成可信区域和不可信区域。
可信区域,就是平台自己,或者友商建立的系统,可以保证从这些系统中获取并传递的数据是真实的、可信的。
获取来源IP的正确方式,是提取并记录本次请求首次进入可信区域时的remote address。不论这个IP是不是代理。
XFF伪造的情况其实非常普遍,也陆续地出现了一些替代方案,我司目前使用的,是设置一个专用的字段来传递这个IP,不会和XFF相覆盖。
此外,某些CDN服务商,会有自己定制化的Header字段,情况比较多,建议结合具体的情况来决定如何获取用户的来源IP。
比如,之前遇到一个客户,使用了阿里云的SLB负载均衡,SLB会给每一个请求都加上X-Forwarded-For字段,他们自己的反向代理又加一次。那么其实只要获取XFF中倒数第三个IP,作为来源IP即可。
一种参考方式如下:
在反向代理(Nginx)上配置,增加Real-IP字段:
location /{ ... proxy_set_header Real-IP $remote_addr; ...}
业务系统中,获取来源IP的代码如下(Java示例):