17

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

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzAxMTg2MjA2OA%3D%3D&%3Bmid=2649848319&%3Bidx=4&%3Bsn=99e082897df8d2e73d7b1211b7b65342
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

7ZBrEva.gif

作者:蔡永吉  来源: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算法大礼包。
AZ77Vj3.jpg!web

点个在看少个 bug :point_down:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK