基于Spring MVC的客户端真实IP获取方案解析

发布于:2025-04-20 ⋅ 阅读:(69) ⋅ 点赞:(0)

基于Spring MVC的客户端真实IP获取方案解析

概述

在Web应用开发中,准确获取客户端真实IP地址是常见的需求。本文介绍一个基于Spring MVC实现的客户端IP获取方案ClientIpController,该方案支持多种代理场景下的IP识别,并包含完善的校验机制。

核心方法解析

代码实现

/**
 * 获取客户端公网IP
 *
 * @author xdr630
 */
@RestController
public class ClientIpController {

    private static final Logger LOGGER = LoggerFactory.getLogger(KocaClientIpController.class);

    @GetMapping("/getRemoteIp")
    @ResponseBody
    public String getClientIp(HttpServletRequest request) {

        String clientIp = null;

        try {
            String xForwardedFor = request.getHeader("X-Forwarded-For");
            if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
                // X-Forwarded-For:Squid 服务代理,X-Forwarded-For 可能包含多个 IP,取第一个非空的 IP
                clientIp = xForwardedFor.split(",")[0].trim();
            }

            if (clientIp == null || clientIp.isEmpty() || "unknown".equalsIgnoreCase(clientIp)) {
                // Proxy-Client-IP:apache 服务代理
                clientIp = request.getHeader("Proxy-Client-IP");
            }

            if (clientIp == null || clientIp.isEmpty() || "unknown".equalsIgnoreCase(clientIp)) {
                // WL-Proxy-Client-IP:weblogic 服务代理
                clientIp = request.getHeader("WL-Proxy-Client-IP");
            }

            if ((clientIp == null || clientIp.isEmpty() || "unknown".equalsIgnoreCase(clientIp))) {
                // X-Real-IP:nginx服务代理
                clientIp = request.getHeader("X-Real-IP");
            }

            if (clientIp == null || clientIp.isEmpty() || "unknown".equalsIgnoreCase(clientIp)) {
                clientIp = request.getRemoteAddr();
            }

            if (clientIp != null && !clientIp.isEmpty()) {
                clientIp = clientIp.split(",")[0];
            }

            if (!isValidIPv4(clientIp)) {
                LOGGER.warn("无效的客户端 IP: {}", clientIp);
                // 在浏览器端显示返回null
                clientIp = "null";
            }
        } catch (Exception e) {
            LOGGER.error("获取客户端公网IP失败", e);
            throw new RuntimeException(e);
        }

        return clientIp;
    }

    private boolean isValidIPv4(String ip) {
        String ipv4Regex = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
        return Pattern.compile(ipv4Regex).matcher(ip).matches();
    }
}

工作流程

  1. 多级代理支持:依次检查以下请求头

    • X-Forwarded-For:处理Squid等代理的逗号分隔IP
    • Proxy-Client-IP:Apache代理
    • WL-Proxy-Client-IP:WebLogic代理
    • X-Real-IP:Nginx代理
  2. 最终回退机制

clientIp = request.getRemoteAddr();
  1. 结果处理
    • 取首个有效IP段
    • IPv4格式校验
    • 异常IP返回"null"字符串

IP获取优先级策略

优先级 Header名称 代理类型
1 X-Forwarded-For 通用代理/Squid
2 Proxy-Client-IP Apache
3 WL-Proxy-Client-IP WebLogic
4 X-Real-IP Nginx
5 getRemoteAddr() 直接连接

IP有效性验证

采用正则表达式校验IPv4格式:

^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$

异常处理与日志

  • WARN级别日志:记录无效IP格式
  • ERROR级别日志:捕获处理异常
  • 异常转换:将检查异常转为RuntimeException

使用场景

  1. 反向代理架构下的真实IP获取
  2. 客户端地理位置服务
  3. 访问频率控制
  4. 安全审计日志

注意事项

  1. 代理链中的首个IP可能不可信(需结合安全策略)
  2. 不支持IPv6地址(需扩展正则校验)
  3. 生产环境建议添加速率限制
  4. 重要场景建议结合XFF白名单验证

扩展建议

  1. 增加IPv6支持
  2. 添加可信代理IP列表验证
  3. 集成缓存机制防止滥用
  4. 补充单元测试覆盖边界情况

该方案提供了可靠的客户端IP获取实现,开发人员应根据实际网络架构调整header的检查顺序,并在安全敏感场景中补充额外的验证逻辑。


网站公告

今日签到

点亮在社区的每一天
去签到