最后更新于

使用WAF之后如何获得真实IP

分类: WAF 后端

使用WAF(Web应用防火墙)后,相当于在客户端和服务器之间增加了一层代理。后端的nginx或应用服务直接获取到的IP实际上是WAF的回源IP,而不是终端用户的真实IP。

🔍 WAF真实IP传递机制

WAF会将客户端真实IP放到HTTP请求头中,不同厂商使用的Header略有差异:

WAF厂商Header字段
阿里云、华为云X-Forwarded-For
阿里云X-Real-IP
CloudflareCF-Connecting-IP

🔧 Nginx日志配置

可以在nginx侧配置日志来测试真实IP获取:

注意事项:

  • Header需要加上 http_ 前缀
  • 横杠需要改为下划线
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for" "$http_x_real_ip"';

☕ Java获取真实IP

使用Hutool工具类

结合hutool工具类的具体实现:

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.servlet.ServletUtil;

public class IpAddressUtil {

    private static final Log log = Log.get();

    private static final String LOCAL_IP = "127.0.0.1";

    private static final String LOCAL_REMOTE_HOST = "0:0:0:0:0:0:0:1";

    /**
     * 获取客户端IP
     */
    public static String getIp(HttpServletRequest request) {
        if (ObjectUtil.isEmpty(request)) {
            return LOCAL_IP;
        } else {
            String remoteHost = ServletUtil.getClientIP(request);
            return LOCAL_REMOTE_HOST.equals(remoteHost) ? LOCAL_IP : remoteHost;
        }
    }
}

Hutool实现原理

Hutool通过以下Header顺序获取客户端IP:

public static String getClientIP(HttpServletRequest request, String... otherHeaderNames) {
    String[] headers = new String[]{
        "X-Forwarded-For",
        "X-Real-IP",
        "Proxy-Client-IP",
        "WL-Proxy-Client-IP",
        "HTTP_CLIENT_IP",
        "HTTP_X_FORWARDED_FOR"
    };
    if (ArrayUtil.isNotEmpty(otherHeaderNames)) {
        headers = (String[])ArrayUtil.addAll(new String[][]{headers, otherHeaderNames});
    }

    return getClientIPByHeader(request, headers);
}

调试接口

用于快速测试各个Header的值:

@GetMapping("/getClientIp")
public String testClientIp(HttpServletRequest request) {
    String[] headers = {
        "X-Forwarded-For",
        "X-Real-IP",
        "Proxy-Client-IP",
        "WL-Proxy-Client-IP",
        "HTTP_CLIENT_IP",
        "HTTP_X_FORWARDED_FOR"
    };
    StringBuilder sb = new StringBuilder();
    for (String header : headers) {
        sb.append(header)
          .append(": ")
          .append(ServletUtil.getClientIPByHeader(request, header))
          .append("\n");
    }
    return sb.toString();
}

🌐 使用ngx_http_realip_module模块

Cloudflare配置示例

在nginx的http节点下配置:

# 启用 ngx_http_realip_module 模块
real_ip_header CF-Connecting-IP;
#real_ip_header X-Forwarded-For;

# 设置 Cloudflare 的 IP 地址段
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
# ... 更多IP段

IP地址列表更新

⚠️ 重要提醒:Cloudflare IP地址列表需要定期更新,以包含最新的IP地址段。

获取最新IP列表:

配置完成后,真实IP会存储到 $remote_addr 变量中。

调试配置

用于快速测试IP获取的nginx配置:

location /getip {
    default_type "text/plain;charset=utf-8";
    return 200 "$remote_addr";
}

💡 提示:建议定期检查和更新WAF厂商的IP地址段,确保真实IP获取的准确性。