前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >客户端IP获取

客户端IP获取

作者头像
码客说
发布2021-02-25 10:08:20
4.2K0
发布2021-02-25 10:08:20
举报
文章被收录于专栏:码客码客

概念

X-Forwarded-For

X-Forwarded-For标准格式如下:

X-Forwarded-For: client1, proxy1, proxy2

从标准格式可以看出,X-Forwarded-For头信息可以有多个,中间用逗号分隔,第一项为真实的客户端ip,剩下的就是曾经经过的代理或负载均衡的ip地址,经过几个就会出现几个。

X-Forwarded-For和X-Real-IP

  1. X-Forwarded-For是用于记录代理信息的,每经过一级代理(匿名代理除外),代理服务器都会把这次请求的来源IP追加在X-Forwarded-ForX-Real-IP一般只记录真实发出请求的客户端IP
  2. X-Forwarded-For在正向(如squid)反向(如nginx)代理中都是标准用法, 而正向代理中是没有X-Real-IP相关的标准的,也就是说,如果用户访问你的 nginx反向代理之前,还经过了一层正向代理,你即使在nginx中配置了X-Real-IP,取到的也只是正向代理的IP而不是客户端真实IP
  3. 大部分nginx反向代理配置文章中都没有推荐加上X-Real-IP ,而只有X-Forwarded-For,因此更通用的做法自然是取X-Forwarded-For
  4. 多级代理很少见,只有一级代理的情况下二者是等效的
  5. 如果有多级代理,X-Forwarded-For效果是大于X-Real-IP的,可以记录完整的代理链路

相关请求头

  • X-Forwarded-For :这是一个 Squid 开发的字段,只有在通过了HTTP代理或者负载均衡服务器时才会添加该项。 格式为X-Forwarded-For:client1,proxy1,proxy2,一般情况下,第一个ip为客户端真实ip,后面的为经过的代理服务器ip。现在大部分的代理都会加上这个请求头。
  • Proxy-Client-IP/WL- Proxy-Client-IP :这个一般是经过apache http服务器的请求才会有,用apache http做代理时一般会加上Proxy-Client-IP请求头,而WL-Proxy-Client-IP是他的weblogic插件加上的头。
  • HTTP_CLIENT_IP :有些代理服务器会加上此请求头。
  • X-Real-IP :Nginx代理一般会加上此请求头。

后端能获取到的三个IP地址

代码语言:javascript
复制
request.getHeader("X-Forwarded-For");
request.getHeader("X-Real-IP");
request.getRemoteAddr();

获取IP的接口

https://www.ip.cn/api/index?ip=&type=0

返回的数据格式

代码语言:javascript
复制
{"rs":1,"code":0,"address":"中国  河南省 郑州市 电信","ip":"123.53.36.16","isDomain":0}

Nginx代理获取到的IP

假如我们的请求经过如下反向代理

请求 => proxy1 => proxy2 => proxy3 => 后端服务

假如

  • 请求的IP为6.6.6.6
  • proxy1的IP为1.1.1.1
  • proxy2的IP为2.2.2.2
  • proxy3的IP为3.3.3.3
  • 后端服务的IP为8.8.8.8

$remote_addr获取到的地址

proxy1:6.6.6.6 proxy2:1.1.1.1 proxy3:2.2.2.2

结果

只有第一个代理能获取到真实的IP

$proxy_add_x_forwarded_for获取到的地址

proxy1:6.6.6.6 proxy2:6.6.6.6,1.1.1.1 proxy3:6.6.6.6,1.1.1.1,2.2.2.2

proxy1、2、3 的配置中都加上:

代码语言:javascript
复制
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

结果

获取到的IP中第一个都是真实的IP

但客户端请求头中人为添加:X-Forwarded-For=192.168.1.1,192.168.1.2,再看看结果:

proxy1:192.168.1.1,192.168.1.2,6.6.6.6 proxy2:192.168.1.1,192.168.1.2,6.6.6.6,1.1.1.1 proxy3:192.168.1.1,192.168.1.2,6.6.6.6,1.1.1.1,2.2.2.2

结果

第一个IP都不是真实的IP了

怎么获取真实的IP

使用 X-Forwarded-For + realip模块

可以使用nginx的 ngx_http_realip_module 模块,从 X-Forwarded-For 或其他属性中提取真实IP。此处以 X-Forwarded-For 结合该模块为例子,需要做两件事:

一是请求途径的各代理需要设置

代码语言:javascript
复制
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

二是利用 realip 模块获取真实IP

这里proxy3的部分配置(proxy3将请求直接转发到后端服务),如下:

代码语言:javascript
复制
server {
    location / {
        set_real_ip_from 1.1.1.1; 
        real_ip_header X-Forwarded-For;
        real_ip_recursive on;
    }
}
  • set_real_ip_from:表示从何处获取真实IP(解决安全问题,只认可自己信赖的IP),可以是IP或子网等, 可以设置多个set_real_ip_from。
  • real_ip_header:表示从哪个header属性中获取真实IP
  • real_ip_recursive:递归检索真实IP,若从 X-Forwarded-For 中获取,则需递归检索;若像从X-Real-IP中获取,则无需递归。

基于上一步的测试数据,试验结果:

$remote_addr获取到的地址

proxy1:6.6.6.6 proxy2:1.1.1.1 proxy3:6.6.6.6

$proxy_add_x_forwarded_for获取到的地址

proxy1:192.168.1.1,192.168.1.2,6.6.6.6 proxy2:192.168.1.1,192.168.1.2,6.6.6.6,1.1.1.1 proxy3:192.168.1.1,192.168.1.2,6.6.6.6,1.1.1.1,2.2.2.2

此时,proxy3 的 $remote_addr 已经拿到了客户端的真实IP 36.157.229.110,然后 proxy3 将 remote_addr 传递到后端服务中去。

后端获取

代码语言:javascript
复制
request.getRemoteAddr();

使用 X-Forwarded-For + 重置设置

由于客户端可以自行传递X-Forwarded-For,因此,可以在第一个代理处重置其值,达到忽略客户端传递的X-Forwarded-For的效果。

在 proxy1 中进行如下配置:

代码语言:javascript
复制
proxy_set_header X-Forwarded-For $remote_addr;

后端代码

代码语言:javascript
复制
String ip = request.getHeader("X-Forwarded-For");
if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
    //多次反向代理后会有多个ip值,第一个ip才是真实ip
    int index = ip.indexOf(",");
    if(index!=-1){
        return ip.substring(0,index);
    }else{
        return ip;
    }
}

使用 X-Real-IP

由于proxy1的 remote_addr 是客户端真实IP,因此在 proxy1 中将X-Real-IP的值设置为 remote_addr 即可。

代码语言:javascript
复制
proxy_set_header X-Real-IP $remote_addr;

结果为:

$http_x_real_ip获取到的地址

proxy1:- proxy2:6.6.6.6 proxy3:6.6.6.6

proxy1 中设置了X-Real-IP的值,proxy2、proxy3日志中可以看到该值

后端获取

代码语言:javascript
复制
request.getHeader("X-Real-IP");

仅后端判断

仅后端判断,如果人为添加:X-Forwarded-For=192.168.1.1,192.168.1.2这样的头,是没法获取真实的IP的,但是实际使用中我们可忽略,不确定代理的层级可使用如下代码获取

Nginx代理中配置

代码语言:javascript
复制
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Java获取

代码语言:javascript
复制
/**
 * 获取客户端ip地址
 * @param request
 * @return
 */
public static String getRealIp(HttpServletRequest request) {
    String ip = request.getHeader("X-Forwarded-For");
    if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
        // 多次反向代理后会有多个ip值,第一个ip才是真实ip
        if( ip.indexOf(",")!=-1 ){
            ip = ip.split(",")[0];
        }
    }
    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.getHeader("X-Real-IP");
    }
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
        ip = request.getRemoteAddr();
    }
    return ip;
}

附录

Nginx可用变量

http://nginx.org/en/docs/http/ngx_http_core_module.html#variables

搜索 Embedded Variables

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-02-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概念
  • 获取IP的接口
  • Nginx代理获取到的IP
  • 怎么获取真实的IP
    • 使用 X-Forwarded-For + realip模块
      • 使用 X-Forwarded-For + 重置设置
        • 使用 X-Real-IP
          • 仅后端判断
          • 附录
          相关产品与服务
          负载均衡
          负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档