经典工具方法:
public static String getIpAddr(HttpServletRequest request) {
if (request == null) {
return "unknown";
}
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
}
说明:
这段代码的核心逻辑是"层层探测"。因为在复杂的网络环境(经过 CDN、Nginx、防火墙等)中,获取真实 IP 的方式不唯一,所以代码按优先级尝试从不同的 HTTP 头中获取。
无需前台代码特意在请求中添加什么数据,
无论直接访问还是经过代理访问两种情况,后台这段代码都可拿到用户IP:
- 直接访问(无 Nginx/CDN)
过程: 浏览器 \rightarrow Tomcat (Java 服务)
结果: 所有 getHeader(...) 都会是 null 。
最终 IP: 代码会执行到 request.getRemoteAddr() ,直接拿到来自浏览器的 IP。
- 代理访问(有 Nginx/CDN - 最常见的情况)
过程:
浏览器 \rightarrow Nginx
Nginx \rightarrow Tomcat (Java 服务)
关键点: Nginx 在转发请求给 Tomcat 时,会自动修改请求头。
Nginx 配置示例:
location / {
proxy_set_header X-Real-IP $remote_addr; # 把真实IP放入这个头
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 追加IP链
proxy_pass http://tomcat_server;
}
结果: Java 代码在 Tomcat 中运行时,就能从 X-Real-IP 或 X-Forwarded-For 头中读取到 Nginx 传过来的真实 IP。