You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中Service(DAO)类如何正确获取客户端IP地址?

兄弟,你这问题我太懂了!现在你的代码拿到服务器IP完全正常——因为InetAddress.getLocalHost()是在服务器端执行的,它只能获取服务器自身的网络地址,根本碰不到客户端发过来的请求信息。要拿到真正的客户端IP,得从HTTP请求里提取相关信息,再把IP从请求入口(比如Controller层)传递到Service/DAO层才行。下面给你一步步讲解决方案:

第一步:在Controller层获取真实客户端IP

客户端的IP信息会包含在HTTP请求头里,尤其是如果你的服务前面有反向代理(比如Nginx、Apache),直接用request.getRemoteAddr()可能拿到的是代理服务器的IP,所以需要做兼容处理:

public String getClientIpAddress(HttpServletRequest request) {
    // 优先从反向代理头中取真实IP
    String ip = request.getHeader("X-Forwarded-For");
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("X-Real-IP");
    }
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("Proxy-Client-IP");
    }
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("WL-Proxy-Client-IP");
    }
    // 如果上面的头都没有,直接拿请求的远程地址
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getRemoteAddr();
        // 处理本地IPv6的情况,转换成常用的IPv4格式
        if ("0:0:0:0:0:0:0:1".equals(ip)) {
            ip = "127.0.0.1";
        }
    }
    // X-Forwarded-For可能包含多个IP(多代理场景),取第一个非unknown的IP
    if (ip != null && ip.contains(",")) {
        ip = ip.split(",")[0].trim();
    }
    return ip;
}

第二步:修改Service方法,新增IP参数

把你的updateCompanyByNameandUrl方法改成接收客户端IP参数,这样Controller层拿到IP后直接传进来即可:

// 修改后的Service方法,新增userIp参数
public synchronized void updateCompanyByNameandUrl(String companyName, String companyUrl, String userIp) {
    PreparedStatement ps = null;
    PreparedStatement ps1 = null;
    Connection con = null;
    Connection globalconnection = null;
    String updateCompanyquery = "update `anjo_company_master` set URL = ? where COMPANY_NAME=?";
    String updateGlobalCompQuery= "update `anjo_company_master_global` set URL = ?, USER_IP = ? where COMPANY_NAME=?";
    try {
        // 这里不再需要本地获取IP,直接使用传入的客户端IP
        con = SQLHikariConnectionFactory.getInstance().getConnection();
        ps = con.prepareStatement(updateCompanyquery);
        ps.setString(1, companyUrl.trim());
        ps.setString(2, companyName.trim());
        ps.executeUpdate();
        
        // 更新全局表
        globalconnection = connFactory.getGlobalCompanyConnection();
        ps1 = globalconnection.prepareStatement(updateGlobalCompQuery);
        ps1.setString(1, companyUrl.trim());
        ps1.setString(2, userIp.trim());
        ps1.setString(3, companyName.trim());
        ps1.executeUpdate();
    } catch(Exception e) {
        e.printStackTrace();
        LOG.info(e.getMessage(), e);
    } finally {
        try {
            // 别忘了关闭所有资源,包括globalconnection和ps1
            if (con != null) con.close();
            if (ps != null) ps.close();
            if (globalconnection != null) globalconnection.close();
            if (ps1 != null) ps1.close();
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }
}

第三步:在Controller中调用Service时传入IP

在你的Controller方法里,先调用上面的getClientIpAddress方法拿到客户端IP,再传给Service:

@Autowired
private YourCompanyService companyService;

@RequestMapping("/updateCompany")
public String updateCompany(HttpServletRequest request, 
                            @RequestParam String companyName, 
                            @RequestParam String companyUrl) {
    // 获取客户端真实IP
    String clientIp = getClientIpAddress(request);
    // 调用Service方法时传入IP
    companyService.updateCompanyByNameandUrl(companyName, companyUrl, clientIp);
    // 后续处理逻辑...
    return "success";
}

额外注意事项

  • 如果你的服务没有经过反向代理,直接用request.getRemoteAddr()也能拿到客户端IP,但加上上面的兼容逻辑会更健壮,避免后续加代理后出问题。
  • 如果用了反向代理(比如Nginx),要确保代理服务器正确设置了X-Forwarded-For和X-Real-IP头,比如Nginx配置里要加:
    proxy_set_header X-Forwarded-For $remote_addr;
    proxy_set_header X-Real-IP $remote_addr;
    
  • 尽量不要在Service/DAO层直接通过RequestContextHolder获取HttpServletRequest,这种方式会让Service层耦合HTTP上下文,不符合分层设计的原则,最好还是从上层传递参数。

内容的提问来源于stack exchange,提问作者Kanchikatkar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 08:27:39