Nginx获取真实ip和客户端端口

业务场景

# request.getRemoteAddr()

# 介绍

该接口返回发送请求的客户端或最后一个代理的 Internet 协议 (IP) 地址。
https://docs.oracle.com/javaee/7/api/javax/servlet/ServletRequest.html (opens new window)

# 使用

在客户端直接访问服务端(中间没有代理)时,可以通过这种方式正确拿到客户端IP。当中间存在代理服务器,如nginx时,拿到的将是最近一个代理服务器的IP。

# X-Real-IP $remote_addr

# 介绍

虽然通过request.getRemoteAddr()无法获得用户的真实IP,但是,nginx是可以获得用户的真实ip的,也就是说nginx使用$remote_addr变量时获得的是用户的真实ip

# 使用

proxy_set_header X-Real-IP $remote_addr;
1

# X-Forwarded-For

# 介绍

X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP头字段。Squid缓存代理服务器的开发人员最早引入了这一HTTP头字段。
https://zh.wikipedia.org/wiki/X-Forwarded-For (opens new window)

# 格式

X-Forwarded-For: client1, proxy1, proxy2
1

其中的值通过一个 逗号+空格 把多个IP地址区分开, 最左边(client1)是最原始客户端的IP地址, 代理服务器每成功收到一个请求,就把请求来源IP地址添加到右边。
在上面这个例子中,这个请求成功通过了三台代理服务器:proxy1、proxy2和proxy3。请求由client1发出,到达了proxy3(proxy3可能是请求的终点)。
请求刚从client1中发出时,XFF是空的,请求被发往proxy1;
通过proxy1的时候,client1被添加到XFF中,之后请求被发往proxy2;
通过proxy2的时候,proxy1被添加到XFF中,之后请求被发往proxy3;
通过proxy3时,proxy2被添加到XFF中,之后请求的的去向不明,如果proxy3不是请求终点,请求会被继续转发。
鉴于伪造这一字段非常容易,应该谨慎使用X-Forwarded-For字段。正常情况下XFF中最后一个IP地址是最后一个代理服务器的IP地址, 这通常是一个比较可靠的信息来源。

# 使用

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
1

$proxy_add_x_forwarded_for会将和Nginx直接连接的客户端IP追加在请求原有X-Forwarded-For值的右边。

如果一个HTTP请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP分别为 IP1、IP2、IP3,用户真实IP为IP0,那么拿到的值为X-Forwarded-For: IP0, IP1, IP2

# 代码

public class RequestUtil {
  public RequestUtil() {
  }

  public static String getIpAddr(HttpServletRequest request) {
    if (request == null) {
      return null;
    } else {
      String ip = null;
      if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
          ip = request.getHeader("X-Forwarded-For");
          if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
              ip = request.getRemoteAddr();
            }
          }
        }
      }

      if (!StringUtils.isEmpty(ip)) {
        ip = ip.split(",")[0];
      }

      return ip;
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# X-Forwarded-Port

# 使用

proxy_set_header X-Forwarded-Port $remote_port;
1

# 代码

public class RequestUtil {

  private static final String X_FORWARDED_PORT = "X-Forwarded-Port";

  private RequestUtil() {
  }

  public static int getClientPort(HttpServletRequest request) {
    int remotePort = 0;

    if(request != null) {
      String port = request.getHeader(X_FORWARDED_PORT);
      log.info("============>X-Forwarded-Port: {}", port);
      if(StringUtil.isNotBlank(port)) {
        remotePort = Integer.parseInt(port.split(",")[0]);
      } else {
        remotePort = request.getRemotePort();
      }
    }
    log.info("============>remotePort: {}", remotePort);
    return remotePort;
  }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

参考: https://blog.csdn.net/qq_34556414/article/details/78185057 (opens new window)
https://blog.csdn.net/bao19901210/article/details/52537279 (opens new window)
https://www.nginx.cn/doc/standard/httpproxy.html (opens new window)
https://blog.csdn.net/wanglei_storage/article/details/66004933 (opens new window)
https://www.cnblogs.com/sucretan2010/p/12522851.html (opens new window)
https://blog.csdn.net/juewuer/article/details/104850405 (opens new window)
https://docs.ifs.com/techdocs/Foundation1/010_overview/210_security/090_exposing_to_internet/sample_nginx.htm (opens new window)
http://nginx.org/en/docs/http/ngx_http_core_module.html#server (opens new window)
http://nginx.org/en/docs/varindex.html (opens new window)
http://nginx.org/en/docs/http/ngx_http_core_module.html#var_remote_port (opens new window)