作者:蔡永吉 来源: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)同时获取表的架构实例
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Domain-Driven Design
Eric Evans / Addison-Wesley Professional / 2003-8-30 / USD 74.99
"Eric Evans has written a fantastic book on how you can make the design of your software match your mental model of the problem domain you are addressing. "His book is very compatible with XP. It is n......一起来看看 《Domain-Driven Design》 这本书的介绍吧!