如何在 servlet 中获取客户端的远程地址?

有没有什么办法可以得到客户端到服务器的原始 IP 地址? 我可以使用 request.getRemoteAddr(),但我似乎总是得到代理或网络服务器的 IP。

我想知道客户端用来连接到我的 IP 地址。有什么办法能让我拿到吗?

84684 次浏览

request.getRemoteAddr() is the way. It appears your proxy changes the source IP. When some proxies do that they add the original IP in some custom http header. Use request.getHeaderNames() and request.getHeaders(name) and print all of them to see if there isn't anything of interest. Like X-CLIENT-IP (made that one up, but they look like this)

You cannot do this in a meaningful way.

The proxy may or may not add a proxied-for header, but in many cases this will be an internal only address anyway, so it will be meaningless to you. Most proxies at the edge of an organization are configured to reveal as little as possible about the internals of the network anyway.

What are you intending to use this information for?

try this:

public static String getClientIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
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.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
String ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null) {
ipAddress = request.getHeader("X_FORWARDED_FOR");
if (ipAddress == null){
ipAddress = request.getRemoteAddr();
}
}

"x-forwarded-for" request header contains the original client IP if using a proxy or a load balancer. But I think not all proxies/lb adds this header.

Here some java code to parse the header: http://www.codereye.com/2010/01/get-real-ip-from-request-in-java.html

If this header is not present then I would proceed as @Bozho suggests

The best solution I've ever used

public String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
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();
}
return ip;
}

As this is usually a deployment concern, rather than an application concern, another approach would be to configure the application container appropriately. Once configured, the container takes care of inspecting the appropriate header and your application continues to use request.getRemoteAddr().

For example, in Tomcat you can use the Remote IP Valve. I would assume most application servers have similar functionality.

The container could also take care of understanding if your front-end load balancer is terminating SSL connections, forwarding the request to the app server over HTTP. This is important when your application needs to generate URLs to itself.

InetAddress inetAddress = InetAddress.getLocalHost();
String ip = inetAddress.getHostAddress();

Why don't use a more elegant solution like this?

private static final List<String> IP_HEADERS = Arrays.asList("X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR");


public static String getClientIpAddr(HttpServletRequest request) {
return IP_HEADERS.stream()
.map(request::getHeader)
.filter(Objects::nonNull)
.filter(ip -> !ip.isEmpty() && !ip.equalsIgnoreCase("unknown"))
.findFirst()
.orElseGet(request::getRemoteAddr);
}

Deduplicate your code!

Why I think we should try to get IP from header 'X-Forwarded-For' first? If you get from request.getRemoteAddr(), it could be client's real ip or last proxy's ip which forwards the request. Thus we can't tell which condition it belongs to. However, if 'X-Forwarded-For' is set into the header, client ip is bound to be the left-most part of what you get from it.

    /**
* Try to get real ip from request:
* <ul>
*     <li>try X-Forwarded-For</li>
*     <li>try remote address</li>
* </ul>
*
* @param request    request
* @return real ip or ""
*/
private String tryGetRealIp(HttpServletRequest request) {
// X-Forwarded-For: <client>, <proxy1>, <proxy2>
// If a request goes through multiple proxies, the IP addresses of each successive proxy is listed.
// This means, the right-most IP address is the IP address of the most recent proxy and
// the left-most IP address is the IP address of the originating client.
String forwards = request.getHeader("X-Forwarded-For");
if (StringUtils.isNotBlank(forwards)) {
// The left-most IP must be client ip
String ip = StringUtils.substringBefore(forwards, ",");
return ip;
} else if (StringUtils.isNotBlank(request.getRemoteAddr())) {
// this could be real client ip or last proxy ip which forwards the request
return request.getRemoteAddr();
}
return "";
}

request.getHeader("True-Client-IP")

it will return the client IP address