在我们项目开发中,很多时候需要记录访问ip地址,方便跟踪操作日志定位是哪个机器访问了我们的服务(逮住证据),或者根据ip做一些分析统计。
在 Java 中,获取客户端 IP 地址通常是在处理 Web 请求时进行的。这个过程通常涉及从 HTTP 请求头中提取客户端的 IP 地址。如果你在开发 Web 应用(比如使用 Servlet 或 Spring Boot)
获取客户端 IP 地址的常用方法是通过 HttpServletRequest 的头信息(如 X-Forwarded-For)和 getRemoteAddr()。
在日志中记录 IP 地址有助于分析访问来源,检测异常请求等。
在 Spring Boot 或 Servlet 中,你可以使用 Filter 或 Interceptor 来统一处理并记录 IP 地址。
- 通过 HttpServletRequest 获取 IP 地址
在 Web 应用中,HttpServletRequest 对象提供了获取客户端 IP 地址的方法。通常情况下,客户端 IP 地址会通过 X-Forwarded-For 或 Proxy-Client-IP 等 HTTP 头传递,这些头通常由反向代理服务器(如 Nginx 或 Apache)添加。如果请求没有经过代理服务器,request.getRemoteAddr() 可以直接获取客户端的 IP 地址。
获取客户端 IP 地址的基本方法:
import javax.servlet.http.HttpServletRequest;
public class IpUtils {
public static String getClientIp(HttpServletRequest request) {
// 通过 X-Forwarded-For 获取客户端真实 IP 地址(有可能是多层代理)
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
// 如果没有 X-Forwarded-For 头,则尝试通过其他代理头获取
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
// 如果没有 Proxy-Client-IP 头,则尝试通过其他代理头获取
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
// 最后通过 getRemoteAddr 获取客户端 IP
ip = request.getRemoteAddr();
}
// 如果是通过代理服务器访问,可能会有多个 IP,取第一个非 "unknown" 的 IP
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return ip;
}
}
X-Forwarded-For:当请求通过反向代理或负载均衡器时,这个头包含客户端的原始 IP 地址。它可能会包含多个 IP 地址,多个 IP 地址之间用逗号分隔,顺序从最远到最近。第一个 IP 地址通常是客户端的真实 IP 地址。
Proxy-Client-IP 和 WL-Proxy-Client-IP:这些是某些代理服务器或 Web 服务器可能设置的头。
getRemoteAddr():如果请求没有经过代理,这个方法会返回客户端的直接 IP 地址。
2. 记录日志时记录客户端 IP 地址
当你需要记录日志时,通常会在日志中记录客户端的 IP 地址。你可以通过获取到的 IP 地址来记录访问来源,帮助分析流量、进行安全监控等。
假设你使用 SLF4J 和 Logback 作为日志框架,下面是一个简单的日志记录示例,记录请求的 IP 地址:
使用日志框架记录 IP 地址:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
public class LogUtils {
private static final Logger logger = LoggerFactory.getLogger(LogUtils.class);
public static void logClientIp(HttpServletRequest request) {
String ip = IpUtils.getClientIp(request);
logger.info("Request from IP: " + ip);
}
}
在 Servlet 中使用:
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 记录客户端 IP 地址
LogUtils.logClientIp(request);
// 继续处理请求
response.getWriter().write("Hello, world!");
}
}
- 在 Spring Boot 中获取 IP 地址
如果你在使用 Spring Boot,可以将上述方法稍作修改以适应 Spring Boot 的处理方式。你可以在 Controller 中获取并记录 IP 地址。
Spring Boot 示例:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
public class IpController {
@GetMapping("/get-ip")
public String getClientIp(HttpServletRequest request) {
String ip = IpUtils.getClientIp(request);
// 记录 IP 地址
System.out.println("Request from IP: " + ip);
return "Client IP: " + ip;
}
}
- 日志记录的进一步优化
在生产环境中,你可能希望将 IP 地址与其他请求信息一起记录到日志文件中,以便后续分析。你可以配置日志框架来自动化这个过程,或使用过滤器(Filter)或拦截器(Interceptor)来在所有请求中统一处理 IP 地址的记录。
使用 Filter 来记录 IP 地址:
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter("/*") // 拦截所有请求
public class IpLoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String ip = IpUtils.getClientIp(httpRequest);
// 在日志中记录 IP 地址
System.out.println("Request from IP: " + ip);
// 继续请求的处理
chain.doFilter(request, response);
}
@Override
public void destroy() {}
}