本文目录导读:

- 标准Servlet(Spring MVC/普通Web应用)
- Spring Boot WebFlux(响应式编程)
- Spring Cloud Gateway(过滤器方式)
- 使用Spring AOP统一处理
- 处理IPv6地址(可选)
- Nginx配置配合(关键)
- Spring Boot application.yml配置
- 完整工具类(包含边界处理)
- 注意事项
在Java中获取客户端真实IP需要根据不同的技术栈采用不同的方法,以下是几种常见场景的实现方式:
标准Servlet(Spring MVC/普通Web应用)
import javax.servlet.http.HttpServletRequest;
public class IpUtils {
public static String getClientIp(HttpServletRequest request) {
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("WL-Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_CLUSTER_CLIENT_IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_FORWARDED_FOR");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_FORWARDED");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_VIA");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 处理多级代理的情况(如:X-Forwarded-For: client, proxy1, proxy2)
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return ip;
}
}
使用示例:
@RestController
public class IpController {
@GetMapping("/get-ip")
public String getIp(HttpServletRequest request) {
return "客户端IP: " + IpUtils.getClientIp(request);
}
}
Spring Boot WebFlux(响应式编程)
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class IpWebFluxUtils {
public static Mono<String> getClientIp(ServerWebExchange exchange) {
return Mono.justOrEmpty(exchange.getRequest().getHeaders()
.getFirst("X-Forwarded-For"))
.filter(ip -> !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip))
.map(ip -> ip.split(",")[0].trim())
.switchIfEmpty(
Mono.justOrEmpty(exchange.getRequest().getHeaders()
.getFirst("Proxy-Client-IP"))
)
.switchIfEmpty(
Mono.justOrEmpty(exchange.getRequest().getHeaders()
.getFirst("WL-Proxy-Client-IP"))
)
.switchIfEmpty(
Mono.just(exchange.getRequest().getRemoteAddress()
.map(address -> address.getAddress().getHostAddress())
.orElse(""))
);
}
}
使用示例:
@RestController
public class IpWebFluxController {
@GetMapping("/get-ip")
public Mono<String> getIp(ServerWebExchange exchange) {
return IpWebFluxUtils.getClientIp(exchange);
}
}
Spring Cloud Gateway(过滤器方式)
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class IpGatewayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String ip = getIpFromRequest(request);
// 将IP添加到请求头中,传递给下游服务
ServerHttpRequest modifiedRequest = request.mutate()
.header("X-Real-IP", ip)
.build();
ServerWebExchange modifiedExchange = exchange.mutate()
.request(modifiedRequest)
.build();
return chain.filter(modifiedExchange);
}
private String getIpFromRequest(ServerHttpRequest request) {
String ip = request.getHeaders().getFirst("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeaders().getFirst("X-Real-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddress() != null
? request.getRemoteAddress().getAddress().getHostAddress()
: "";
}
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return ip;
}
@Override
public int getOrder() {
return Integer.MIN_VALUE;
}
}
使用Spring AOP统一处理
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Aspect
@Component
public class IpAspect {
@Before("execution(* com.yourpackage.controller.*.*(..))")
public void setIpToThreadLocal() {
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
String ip = IpUtils.getClientIp(request);
// 存储到ThreadLocal或MDC中,供后续使用
IpContextHolder.setIp(ip);
}
}
}
处理IPv6地址(可选)
public static String getRealIp(HttpServletRequest request) {
String ip = getClientIp(request);
// 处理IPv6本地地址
if (ip != null && ip.startsWith("[") && ip.endsWith("]")) {
ip = ip.substring(1, ip.length() - 1);
}
// IPv6环回地址
if ("0:0:0:0:0:0:0:1".equals(ip) || "::1".equals(ip)) {
ip = "127.0.0.1";
}
return ip;
}
Nginx配置配合(关键)
需要在Nginx中添加配置,确保真实IP能传递到后端:
server {
listen 80;
server_name example.com;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_pass http://your-backend;
}
}
Spring Boot application.yml配置
server: # 如果使用反向代理,需要配置信任的代理 forward-headers-strategy: framework # 或者 # forward-headers-strategy: native
完整工具类(包含边界处理)
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class IpUtils {
private static final String[] IP_HEADERS = {
"X-Forwarded-For",
"Proxy-Client-IP",
"WL-Proxy-Client-IP",
"HTTP_X_FORWARDED_FOR",
"HTTP_X_FORWARDED",
"HTTP_X_CLUSTER_CLIENT_IP",
"HTTP_CLIENT_IP",
"HTTP_FORWARDED_FOR",
"HTTP_FORWARDED",
"HTTP_VIA",
"REMOTE_ADDR"
};
public static String getClientIp(HttpServletRequest request) {
String ip = null;
for (String header : IP_HEADERS) {
ip = request.getHeader(header);
if (isValidIp(ip)) {
break;
}
}
if (ip == null || ip.isEmpty()) {
ip = request.getRemoteAddr();
}
// 处理多级代理
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
// 处理IPv6
ip = normalizeIp(ip);
return ip;
}
private static boolean isValidIp(String ip) {
return ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip);
}
private static String normalizeIp(String ip) {
if (ip == null) return null;
// 处理IPv6环回地址
if ("0:0:0:0:0:0:0:1".equals(ip) || "::1".equals(ip)) {
return "127.0.0.1";
}
// 处理IPv6格式
if (ip.startsWith("[") && ip.endsWith("]")) {
ip = ip.substring(1, ip.length() - 1);
}
// 处理IPv4-mapped IPv6地址
if (ip.startsWith("::ffff:")) {
ip = ip.substring(7);
}
return ip;
}
public static boolean isInternalIp(String ip) {
try {
InetAddress inet = InetAddress.getByName(ip);
return inet.isSiteLocalAddress() || inet.isLoopbackAddress();
} catch (UnknownHostException e) {
return false;
}
}
}
注意事项
- 安全性:不要完全信任HTTP头中的IP,尤其是
X-Forwarded-For可以被伪造 - 性能:避免在频繁调用的方法中重复解析IP
- 代理环境:确保反向代理配置了正确的请求头传递
- IPv6兼容:现代应用需要考虑IPv6的兼容性
- 容器环境:在Docker、Kubernetes等环境中,可能需要特殊处理
选择哪种方式取决于你的具体应用场景和技术栈,最常用的是第一种标准Servlet方式,配合Nginx等反向代理的配置。