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
相关产品推荐
相关产品推荐

