怎么获取一个客户端ip ? 我想这个问题,在网上遍地都是答案!

  而且多半是像下面这样:

    public static String getIpAddress(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;
}

  我也没说要去研究这玩意,我只是不想重复造轮子,然后找到了代码仓库里有这么一段代码,所以,我就用来去获取ip了!

  测试环境一切ok。然后稀拉拉地,上线了!

  嘿,一上线之后,发现了数据库ip字段竟然有两个ip: 10.11.0.6, 202.116.0.83 。

  好嘛,一看就知道是怎么回事了,这个用户的请求是通过代理进来的,而代理只是一种很正常的转发行为,所以必须处理这种情况!

由于原来我设置的ip字段大小为varchar(32), 所以装下这两个ip,是松的事! 大概查了下日志,并没有发现什么异常!

  我想着吧,一般的用户也就是一级代理下,就差不多了。所以应该不会有什么问题,这个问题留给下个版本修复吧!

  我就这么想着,玩去了。

  然后,就被邮件报警了!数据库插入失败!

  妈蛋,该来的始终要来!我还是太年轻了。

有一句叫: 如果你发现有个问题可能会发生,那么它就一定会发生!

  不要侥幸,没人能跑得掉!

  修复办法自然简单到没朋友,截取第一个',' 前的ip就行了!

    public static String getIpAddress(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();
} // 多级代理问题修复
if (ip.indexOf(',') > 0) {
ip = ip.substring(0, ip.indexOf(',')).trim();
} return ip;
}

  好了,问题解决了,警告咱们要有敬畏之心。

下面,咱们来看看多级代理的ip是怎么回事?

  在 nginx 中,咱们可以这么设置:

    location /api {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://10.11.20.22/api;
}

  这样的话,后续服务端就可以通过获取 X-Forwarded-For 来进行获取所有的代理ip地址链了!
  格式为: X-Forwarded-For:10.11.247.1, 10.11.38.131, 10.11.255.1 ...

  也就是说 X-Forwarded-For 是呈叠加的方式的,所以,我们应该只需要取到 第一个ip 就可以了!

  事实上,X-Forwarded-For 是可以伪造的。

  比如: curl -H "X-Forwarded-For:11.11.22.22" http://a.com/api

  可以看到,确实很容易就进行伪造了。而按照后端代理服务器的设置,其只会往该header里添加自己的值,所以,如果此时咱们按照获取第一个值为ip,也就判断失误了。不过这种失误,一般我们还是可以接受的。不过有的场景就不适用了,比如我们通过ip来做权限管理时!所以,在做ip白名单时,还要考虑实际情况而行了!

好了,看得出通过代理标识获取ip是不可靠的,那么,有没有一种可靠的获取ip的方式呢?

  其实是一个值是不可以改的:REMOTE_ADDR. 这个字段源于 tcp/udp 请求中,就会带有源地址,目标地址!

  REMOTE_ADDR 是你的客户端跟你的服务器“握手”时候的IP。但是如果你使用了“匿名代理”,REMOTE_ADDR将显示代理服务器的IP。

  所以,REMOTE_ADDR 虽然是不可改的,但是它却只能代表一级服务器ip,而面对现在复杂的网络环境,那是太无能为力了!

  所以,没办法,还得通过约定的变量来,而这个变量又要依赖于使用的代理设置了。如上!虽然可以伪造,但是至少绝大部分是对的!

最后,给一个 tcp 发送数据样例( src -> dst):

客户端ip获取蹲坑启示: 不要侥幸的更多相关文章

  1. 获取请求Requst中访问请求的客户端IP

    获取请求Request中访问请求的客户端IP /*获取请求客户端的IP地址*/ public static String getIpAddress(HttpServletRequest request ...

  2. PHP获取客户端IP

    /** * 获取客户端IP */ function getClientIp() { $ip = 'unknown'; $unknown = 'unknown'; if (isset($_SERVER[ ...

  3. C#服务器获取客户端IP地址以及归属地探秘

    背景:博主本是一位Windows桌面应用程序开发工程师,对网络通信一知半解.一日老婆逛完某宝,问:"为什么他们知道我的地址呢,他们是怎么获取我的地址的呢?" 顺着这个问题我们的探秘 ...

  4. 在Thinkphp3.2.3框架下实现自动获取客户端IP地址的get_client_ip()函数

    在Thinkphp框架下使用get_client_ip()函数获取客户端IP地址十分方便: 一行代码便可以实现:$ip = get_client_ip(); 但当我们测试时会遇到后台获取的IP地址显示 ...

  5. 通过nginx代理之后,获取客户端ip

    1.相关nginx配置(通过header将客户端ip,host等信息传入) location ~ .*.do$ { proxy_set_header X-Real-IP $remote_addr; p ...

  6. 根据Request获取客户端IP 内网IP及外网IP

    在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr() ,这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实 ...

  7. JAVA获取客户端IP地址

    在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实I ...

  8. nodejs获取客户端IP Address

    在网上看见很多问node.js如何获取客户端IP,所以记录下来,以供大家参考. function getClientIp(req) { return req.headers['x-forwarded- ...

  9. WebService及WCF获取客户端IP,端口

    wcf获取客户端IP,端口 var context = OperationContext.Current; var properties = context.IncomingMessageProper ...

随机推荐

  1. centos7 安装 redis4.0.8

    1.安装lrzsz yum install lrzsz -y 2.利用rz命令将window中从redis官网下载好的“redis-4.0.8.tar.gz” 拷贝到centos中 redis官网 : ...

  2. hbase-bloom filter

    bloom fliter的作用主要用于提升hbase的读性能,但是会牺牲一定的存储空间. 原理: bloom fliter是一种空间效率很高的随机数据结构,初始状态时,bloom filter是一个包 ...

  3. java之数据库相关

    这篇还是在回顾知识.主要是关于java连接Sqlserver2012数据库的一些方式记录,以便以后查询. 十一之内复习完这些知识就可以新学Hibernate啦(*^▽^*) 1.普通方式 注意,在连接 ...

  4. python 15 常用模块三 re模块

    一.正则模块 正则表达式(或 RE)是一种小型的.高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现.正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹 ...

  5. windows任务栏IDEA图标变白色快速解决方法

    方案1:同时按Windows键+R键打开运行对话框,输入ie4uinit.exe -show然后回车即可修复. 方案2:打开计算机(Win7),此电脑(Win10)或任意文件夹,然后在地址栏输入cmd ...

  6. OO第二单元多线程电梯总结

    OO第二单元多线程电梯总结 第一次作业 设计思路 Input为输入线程,负责不断读取请求并将读到的请求放入调度器中. Dispatcher为调度器,是Input线程和Elevator线程的共享对象,采 ...

  7. Vue源码解析---数据的双向绑定

    本文主要抽离Vue源码中数据双向绑定的核心代码,解析Vue是如何实现数据的双向绑定 核心思想是ES5的Object.defineProperty()和发布-订阅模式 整体结构 改造Vue实例中的dat ...

  8. ubuntu18.04静态ip设置

    1.配置文件 vi /etc/netplan/-cloud-init.yaml network: ethernets: enp129s0f0: addresses: [] dhcp4: true op ...

  9. jquery购物车计算总价

    //计算总价 function cartTotal(){ var total = 0; //循环计算的列,选中计算的数量和价格 //accAdd为精BigDecimal准计算方法 $.each($(& ...

  10. spring深入学习(二)-----bean的生命周期、IOC容器bean装配

    bean的生命周期 1.实例化Bean对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBea ...