老大说,网上这种获取真实IP地址的方法不对,我不信...

news/2024/9/20 2:47:38

点击上方“方志朋”,选择“设为星标”

回复”666“获取新整理的面试资料

作者:蔡永吉  来源:http://bit.ly/33H8RMm

想必大家对这段代码并不陌生:

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;
}
是的,你搜索到的“java获取真实IP地址”大多都是如此。但是,以上代码真
的对吗?

那么我们看一下具体的代码。如上,判断ip地址的优先级是

"x-forwarded-for">
"Proxy-Client-IP">
"WL-Proxy-Client-IP">
request.getRemoteAddr()
其中带引号的都是从header中获取的。

等等!我们都知道header中的值是可以更改的。比如:

$.ajax({type : "GET",headers : {"X-Forwarded-For":randomIp,"WL-Proxy-Client-IP":randomIp},contentType : 'application/x-www-form-urlencoded;charset=utf-8',url : url,data:params,dataType : "text",success : function(data) {count++;console.log("时间:【"+new Date()+"】 执行成功:【"+count+"】次:"+data);if(max>0){setTimeout(function wait(){console.log("等待"+(timeWait)+"ms ...");vote(max,getRandomNum(maxWait,minWait));},timeWait);}}}

代码出自:https://github.com/caiyongji/vote-2.0/blob/master/Vote-2.0.js

其中headers属性X-Forwarded-For,WL-Proxy-Client-IP不就是被更改了吗?

那么,为什么会有这个版本的“java获取真实IP地址”的方法呢?并且搜索引擎所能检索到的结果大多都是这一个?

打个比方说,如果这个解决办法是一本秘籍的话,那么,我们找到的只是“java获取真实IP地址”残卷。

而剩下的部分在这里:

#Nginx 设置
location  ~  ^/static {
proxy_pass  ....;
proxy_set_header X-Forward-For $remote_addr ;
}
这段配置是在前端Nginx反向代理上的(其他反向代理请自行搜索),这段配置
作的事情是将X-Forward-For替换为remote_addr,再将X-Forward-For在内网
各服务器间安全传输。

这里我再针对TCP/IP多做一些解释,众所周知TCP/IP建立连接时需要三次握手的,并且,只有知道了client端请求的IP地址,server端的数据才能返回给client,所以client想要获取到数据就必须提供真实的IP(DDOS攻击除外),而request.getRemoteAddr()获取的就是用户最真实的IP。


那么为什么不直接使用使用request.getRemoteAddr()这个方法呢?

如果没有反向代理的话当然可行。但是出于安全原因,现在大多数的服务都使用代理服务器(如Nginx,代理服务器可以理解为用户和服务器之间的中介,双方都可信任。),而用户对代理服务器发起的HTTP请求,代理服务器对服务集群中的真实部署的对应服务进行“二次请求”,所以最终获取的IP是代理服务器在内网中的ip地址,如192.168.xx.xx/10.xx.xx.xx等等。

所以在使用了反向代理的情况下,request.getRemoteAddr()获取的是反响代理在内网中的ip地址。所以在反向代理中将X-Forward-For替换为remote_addr,即,真实的IP地址。之后在内网中获取的x-forwarded-for便是真实的ip地址了。

最后给出完整解决方案(Nginx为例):

JAVA部分:

public class IpUtils {public static final String _255 = "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";public static final Pattern pattern = Pattern.compile("^(?:" + _255 + "\\.){3}" + _255 + "$");public static String longToIpV4(long longIp) {int octet3 = (int) ((longIp >> 24) % 256);int octet2 = (int) ((longIp >> 16) % 256);int octet1 = (int) ((longIp >> 8) % 256);int octet0 = (int) ((longIp) % 256);return octet3 + "." + octet2 + "." + octet1 + "." + octet0;}public static long ipV4ToLong(String ip) {String[] octets = ip.split("\\.");return (Long.parseLong(octets[0]) << 24) + (Integer.parseInt(octets[1]) << 16)+ (Integer.parseInt(octets[2]) << 8) + Integer.parseInt(octets[3]);}public static boolean isIPv4Private(String ip) {long longIp = ipV4ToLong(ip);return (longIp >= ipV4ToLong("10.0.0.0") && longIp <= ipV4ToLong("10.255.255.255"))|| (longIp >= ipV4ToLong("172.16.0.0") && longIp <= ipV4ToLong("172.31.255.255"))|| longIp >= ipV4ToLong("192.168.0.0") && longIp <= ipV4ToLong("192.168.255.255");}public static boolean isIPv4Valid(String ip) {return pattern.matcher(ip).matches();}public static String getIpFromRequest(HttpServletRequest request) {String ip;boolean found = false;if ((ip = request.getHeader("x-forwarded-for")) != null) {StrTokenizer tokenizer = new StrTokenizer(ip, ",");while (tokenizer.hasNext()) {ip = tokenizer.nextToken().trim();if (isIPv4Valid(ip) && !isIPv4Private(ip)) {found = true;break;}}}if (!found) {ip = request.getRemoteAddr();}return ip;}}

Nginx部分:

location  ~  ^/static {
proxy_pass  ....;
proxy_set_header X-Forward-For $remote_addr ;
}
这就是差距啊~~~

热门内容:

  • AJAX 请求真的不安全么?

  • 你知道为什么Java的main方法必须是public static void?

  • 谈谈中间件开发,给想从事中间件开发的同学

  • 大年夜排查bug:竟然是同事把Redis用成这鬼样子,坑了我

  • 代码神器:拒绝重复编码,这款IDEA插件了解一下.....

  • 问题很难,要慌,redis到底单线程还是多线程

  • SpringBoot实现过滤器、拦截器与切片

  • 阿里面试官:分别说说微信和淘宝扫码登录背后的实现原理?

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

明天见(。・ω・。)ノ♡


http://lihuaxi.xjx100.cn/news/275812.html

相关文章

产品与均值比较图

2019独角兽企业重金招聘Python工程师标准>>> 效果图1、绘制辅助数值 在原表中计算出均值这一列 2、鼠标选定数列表&#xff0c;插入柱状图 3、进行比例大小的调整 4、调整背景色 a、 选中对象--Ctrl1--快速的打开对象属性 b、填充--选择一种满意的颜色 5、调整坐标轴…

StringBuilder、StringBuffer、String区别

相信大家对 String 和 StringBuffer 的区别也已经很了解了&#xff0c;但是估计还是会有很多同志对这两个类的工作原理有些不清楚的地方&#xff0c;今天重新把这个概念给大家复习一下&#xff0c;顺便牵出 J2SE5.0 里面带来的一个新的字符操作的类—— StringBuilder &#xf…

微博鸿蒙测试版,不久前华为宣布,将会在6月2日举... - @姬永锋 的微博精选 - 微博国际站...

不久前华为宣布&#xff0c;将会在6月2日举行发布会发布鸿蒙手机&#xff0c;除此之外一大波和鸿蒙系统有关的硬件也会正式登场。对中国消费者、广大科技爱好者来说&#xff0c;“鸿蒙”始终是一个神秘却又充满吸引力的词汇&#xff0c;尤其是华为遭到限制、手机业务面临难题的…

互补输出级采用共集形式是为了使_互补输出级采用共集形式是为了使

【单选题】下面结构中最适于表示稀疏无向图的是( )。【简答题】图的广度优先遍历类似于树的( )遍历,它所用到的数据结构是( )。【填空题】已知L是有表头结点的非空循环单链表,试从下列提供的答案中选择合适的填入空格中。【西南交通大学2004】 (1)删除p结点之后的结点语句序列是…

6个能写进简历的AI项目,有点难,一起来挑战?

NLP进阶之路上&#xff0c;你是否也遇到过这些疑问&#xff1f;为什么在这个问题上使用Adam&#xff0c;而不是GD或者Adagrad? 对于特定的业务场景&#xff0c;我应该如何把领域知识考虑进去&#xff0c; 用先验&#xff0c;还是用限制条件&#xff1f; 对于拼车场景&#xf…

机器学习专业要不要读博?

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶”重磅干货&#xff0c;第一时间送达本文转自|机器学习算法那些事要不要读博&#xff1f;读博值不值得&#xff1f;如何才能顺利完成博士生涯&#xff0c;并为职业发展打好基础&#xff1f;最近&#xff0c;社…

多模态人物识别技术及其在爱奇艺视频场景中的应用 | 公开课笔记

【12月公开课预告】&#xff0c;入群直接获取报名地址12月11日晚8点直播主题&#xff1a;人工智能消化道病理辅助诊断平台——从方法到落地12月12日晚8点直播&#xff1a;利用容器技术打造AI公司技术中台12月17日晚8点直播主题&#xff1a;可重构计算&#xff1a;能效比、通用性…

[Java]学习Java(4)类、接口、语句

1)类多了包的概念2)类继承时与C&#xff0b;&#xff0b;不同&#xff0c;它可以将父类protected的函数重写为public的。3)接口、纯虚函数概念都差不多&#xff0c;语法为&#xff1a;public class A extends B implements IC,ID,IE { ...}4)语句&#xff1a;与C&#xff0b;&a…