Java案例怎么获取客户端IP?

wen java案例 48

本文目录导读:

Java案例怎么获取客户端IP?

  1. 标准Servlet(Spring MVC/普通Web应用)
  2. Spring Boot WebFlux(响应式编程)
  3. Spring Cloud Gateway(过滤器方式)
  4. 使用Spring AOP统一处理
  5. 处理IPv6地址(可选)
  6. Nginx配置配合(关键)
  7. Spring Boot application.yml配置
  8. 完整工具类(包含边界处理)
  9. 注意事项

在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;
        }
    }
}

注意事项

  1. 安全性:不要完全信任HTTP头中的IP,尤其是X-Forwarded-For可以被伪造
  2. 性能:避免在频繁调用的方法中重复解析IP
  3. 代理环境:确保反向代理配置了正确的请求头传递
  4. IPv6兼容:现代应用需要考虑IPv6的兼容性
  5. 容器环境:在Docker、Kubernetes等环境中,可能需要特殊处理

选择哪种方式取决于你的具体应用场景和技术栈,最常用的是第一种标准Servlet方式,配合Nginx等反向代理的配置。

抱歉,评论功能暂时关闭!