作者:蔡永吉 来源: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 ;
}
这就是差距啊~~~
---------- END ----------
推荐一个技术号
这是我好友九哥的公号,他是非科班进微软的资深工程师,公号会分享AI领域的机器学习、深度学习、NLP 、 Python 等前沿知识、技术资讯、干货笔记和优质资源。 关注就无套路送你一份BAT算法大礼包。点个在看少个 bug :point_down:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 【技术小说连载】我在JVM公司的那些年(六)——智斗黑老大
- 慌了,老大让我将线上的服务器升配,咋搞?
- 老大吩咐的可重入分布式锁,终于完美的实现了
- 缓存与数据库双写一致这个 “老大难”,该怎么治
- 老大难的 GC 原理及调优,这下全说清楚了
- ADO.NET获取数据(DataSet)同时获取表的架构实例
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。