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

如果使用了反向代理软件,将http://192.168.1.110:2046/ 的URL反向代理为 http://www.javapeixun.com.cn / 的URL时,用request.getRemoteAddr()方法获取的IP地址是:127.0.0.1 或 192.168.1.110,而并不是客户端的真实IP。

经过代理以后,由于在客户端和服务之间增加了中间层,因此服务器无法直接拿到客户端的IP,服务器端应用也无法直接通过转发请求的地址返回给客户端。但是在转发请求的HTTP头信息中,增加了X-FORWARDED-FOR信息。用以跟踪原有的客户端IP地址和原来客户端请求的服务器地址。当我们访问http://www.javapeixun.com.cn /index.jsp/ 时,其实并不是我们浏览器真正访问到了服务器上的index.jsp文件,而是先由代理服务器去访问http://192.168.1.110:2046/index.jsp ,代理服务器再将访问到的结果返回给我们的浏览器,因为是代理服务器去访问index.jsp的,所以index.jsp中通过request.getRemoteAddr()的方法获取的IP实际上是代理服务器的地址,并不是客户端的IP地址。

于是可得出获得客户端真实IP地址的方法一:

  1. public String getRemortIP(HttpServletRequest request) {
  2. if (request.getHeader("x-forwarded-for") == null) {
  3. return request.getRemoteAddr();
  4. }
  5. return request.getHeader("x-forwarded-for");
  6. }

可是当我访问http://www.5a520.cn /index.jsp/ 时,返回的IP地址始终是unknown,也并不是如上所示的127.0.0.1 或 192.168.1.110了,而我访问http://192.168.1.110:2046/index.jsp 时,则能返回客户端的真实IP地址,写了个方法去验证。原因出在了Squid上。squid.conf 的配制文件 forwarded_for 项默认是为on,如果 forwarded_for 设成了 off  则:X-Forwarded-For: unknown

于是可得出获得客户端真实IP地址的方法二:

  1. public String getIpAddr(HttpServletRequest request) {
  2. String ip = request.getHeader("x-forwarded-for");
  3. if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  4. ip = request.getHeader("Proxy-Client-IP");
  5. }
  6. if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  7. ip = request.getHeader("WL-Proxy-Client-IP");
  8. }
  9. if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  10. ip = request.getRemoteAddr();
  11. }
  12. return ip;
  13. }

可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串Ip值,究竟哪个才是真正的用户端的真实IP呢?

答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。

如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130, 192.168.1.100用户真实IP为: 192.168.1.110

以上方法还不行的话就采用如下方法:

/**
* 从客户端请求中获取客户端的IP
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request){
  String ipAddress = request.getHeader("x-forwarded-for");
  if(isAble(ipAddress)) {
    ipAddress = request.getHeader("Proxy-Client-IP");
  }else if(isAble(ipAddress)) {
    ipAddress = request.getHeader("WL-Proxy-Client-IP");
  }else if (isAble(ipAddress)) {
    ipAddress = request.getHeader("HTTP_CLIENT_IP");
  }else if (isAble(ipAddress)) {
    ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
  }else if (isAble(ipAddress)) {
    ipAddress = request.getRemoteAddr();
  }
  //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
  if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15
    if(ipAddress.indexOf(",")!=-1){
      ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));
    }else{
      ipAddress = ipAddress.substring(0, 15);
    }
  } 
  return ipAddress;
}

/**
* 方法说明:判断ip地址是否可用<br/>
*
* @param ipAddress
* @return
*
*/
public static boolean isAble(String ipAddress){
  return ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress);
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

<以下摘自  http://www.cnblogs.com/zhengyun_ustc/archive/2012/09/19/getremoteaddr.html,感谢原作者>

外界流传的JAVA/PHP服务器端获取客户端IP都是这么取的:
伪代码:
1)ip = request.getHeader("X-FORWARDED-FOR")
    可伪造,参考附录A
2)如果该值为空或数组长度为0或等于"unknown",那么:
ip = request.getHeader("Proxy-Client-IP")
3)如果该值为空或数组长度为0或等于"unknown",那么:
ip = request.getHeader("WL-Proxy-Client-IP")
4)如果该值为空或数组长度为0或等于"unknown",那么:
ip = request.getHeader("HTTP_CLIENT_IP")
    可伪造

5)如果该值为空或数组长度为0或等于"unknown",那么:
ip = request.getRemoteAddr()
    可对于匿名代理服务器,可隐匿原始ip,参考附录B
 
之所以搞这么麻烦,是因为存在很多种网络结构,如 Nginx+Resin、Apache+WebLogic、Squid+Nginx。下面挨个儿讲一下。
郑昀 :△
首先,明确一下,Nginx 配置一般如下所示:
              location / {
                       proxy_pass       http://yourdomain.com;
                       proxy_set_header   Host             $host;
                       proxy_set_header   X-Real-IP        $remote_addr;
                       proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
              }
注意看红色字体,这些配置与下面的闯关拿IP有关。
 
———————————————————————————————
——第一关|X-Forwarded-For :背景——
这是一个 Squid 开发的字段,并非 RFC 标准。
简称 XFF 头,只有在通过了 HTTP 代理或者负载均衡服务器时才会添加该项。在 Squid 开发文档中可以找到该项的详细介绍。
XFF 格式如下:
X-Forwarded-For: client1, proxy1, proxy2
可以看出,XFF 头信息可以有多个,中间用逗号分隔,第一项为真实的客户端ip,剩下的就是曾经经过的代理或负载均衡服务器的ip地址。
——第一关|X-Forwarded-For :场景=客户端--CDN--Nginx——
当用户请求经过 CDN 后到达 Nginx 负载均衡服务器时,其 XFF 头信息应该为 “客户端IP,CDN的IP”。
一般情况下CDN服务商出于自身安全考虑会将屏蔽CDN的ip,只保留客户端ip。
那么请求头到达 Nginx 时:
  • 在默认情况下,Nginx 并不会对 XFF 头做任何处理

    • 此时 Nginx 后面的 Resin/Apache/Tomcat 通过 request.getHeader("X-FORWARDED-FOR") 获得的ip仍然是原始ip
  • 当 Nginx 设置 X-Forwarded-For 等于 $proxy_add_x_forwarded_for 时:
    • 如果从CDN过来的请求没有设置 XFF 头(通常这种事情不会发生),XFF 头为 CDN 的ip

      • 此时相对于 Nginx 来说,客户端就是 CDN
    • 如果 CDN 设置了 XFF 头,我们这里又设置了一次,且值为$proxy_add_x_forwarded_for 的话:
      • XFF 头为“客户端IP,Nginx负载均衡服务器IP”,这样取第一个值即可
      • 这也就是大家所常见的场景!

综上所述,XFF 头在上图的场景,Resin 通过 request.getHeader("X-FORWARDED-FOR") 获得的ip字符串,做一个split,第一个元素就是原始ip。
那么,XFF 头可以伪造吗?
——第一关|X-Forwarded-For :伪造——
可以伪造。
XFF 头仅仅是 HTTP Headers 中的一分子,自然是可以随意增删改的。如附录A所示。

很多投票系统都有此漏洞,它们简单地取 XFF 头中定义的ip地址设置为来源地址,因此第三方可以伪造任何ip投票。
———————————————————————————————
——第二和第三关|Proxy-Client-IP/WL-Proxy-Client-IP :背景——
Proxy-Client-IP 字段和 WL-Proxy-Client-IP 字段只在 Apache(Weblogic Plug-In Enable)+WebLogic 搭配下出现,其中“WL” 就是 WebLogic 的缩写。
即访问路径是:

Client -> Apache WebServer + Weblogic http plugin -> Weblogic Instances

所以这两关对于我们来说仅仅是兼容而已,怕你突然把 Nginx+Resin 换成 Apache+WebLogic 。
也可以直接忽略这两个字段。
———————————————————————————————
——第四关|HTTP-Client-IP :背景——
HTTP_CLIENT_IP 是代理服务器发送的HTTP头。
很多时候 Nginx 配置中也并没有下面这项:

proxy_set_header HTTP_CLIENT_IP $remote_addr;

所以本关也可以忽略。
郑昀 :△

———————————————————————————————
——第五关| request.getRemoteAddr() :背景——
从 request.getRemoteAddr() 函数的定义看:
    Returns the Internet Protocol (IP) address of the client or last proxy that sent the request. 

实际上,REMOTE_ADDR 是客户端跟服务器“握手”时的IP,但如果使用了“匿名代理”,REMOTE_ADDR 将显示代理服务器的ip,或者最后一个代理服务器的ip。请参考附录B。

综上,
java/php 里拿到的ip地址可能是伪造的或代理服务器的ip。
 
郑昀 :△
+++附录A XFF 与 Nginx 配置的测试用例+++
测试环境: nginx+resin
内网IP:172.16.100.10
客户端IP:123.123.123.123

测试页面: test.jsp
<%
out.println("x-forwarded-for: " + request.getHeader("x-forwarded-for"));
out.println("remote hosts: " + request.getRemoteAddr());
%>

nginx 配置一
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
wget测试
wget -O aa --header="X-Forwarded-For:192.168.0.1" "http://test.com/test.jsp"
页面返回结果:
x-forwarded-for: 192.168.0.1, 123.123.123.123
remote hosts: 172.16.100.10
curl测试
curl -H "X-Forwarded-For:192.168.0.1" "http://test.com/test.jsp"
x-forwarded-for: 192.168.0.1, 123.123.123.123
remote hosts: 172.16.100.10

nginx 配置二
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

wget测试:
wget -O aa --header="X-Forwarded-For:192.168.0.1" "http://test.com/test.jsp"
页面返回结果:
x-forwarded-for: 123.123.123.123
remote hosts: 172.16.100.10

curl测试
curl -H "X-Forwarded-For:192.168.0.1" "http://test.com/test.jsp"
x-forwarded-for: 123.123.123.123
remote hosts: 172.16.100.10

测试结果:
1、配置

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
增加了一个真实ip X-Forwarded-For,并且顺序是增加到了“后面”。

2、配置

proxy_set_header X-Forwarded-For $remote_addr;
清空了客户端伪造传入的X-Forwarded-For,
保证了使用 request.getHeader("x-forwarded-for") 获取的ip为真实ip,
或者用“,”分隔,截取 X-Forwarded-For 最后的值。
+++附录B 搜狗浏览器高速模式的测试用例+++
访问路径:

搜狗浏览器“高速”模式(即使用代理)-->LVS-->Apache
获得的值为:
x-forwarded-for:180.70.92.43   (即真实ip)
Proxy-Client-IP:null
WL-Proxy-Client-IP:null 
getRemoteAddr:123.126.50.185  (即搜狗代理ip)
×××参考资源:×××
1,http://bbs.linuxtone.org/thread-9050-1-1.html
2,http://hi.baidu.com/thinkinginlamp/item/e2cf05263eb4d18e6e2cc3e6
3,http://bbs.chinaunix.net/thread-3659453-1-1.html

Struts如何获取客户端ip地址相关推荐

  1. 如何在PHP中获取客户端IP地址[重复]

    本文翻译自:How to get the client IP address in PHP [duplicate] This question already has an answer here: ...

  2. php获取ip几种方法区别,php获取客户端IP地址的几种方法

    php获取客户端IP地址的几种方法 阅读php获取客户端IP地址的几种方法, 1:来看看代码: echo "(1)浏览当前页面的用户的 IP 地址为:"; echo $_SERVE ...

  3. springboot 获取客户端ip_JAVA如何获取客户端IP地址和MAC地址

    本文介绍了JAVA如何获取客户端IP地址和MAC地址 ,分享给大家,具体如下: 1.获取客户端IP地址 public String getIp(HttpServletRequest request) ...

  4. thinkphp 获取客户端ip地址方法

    /*** 获取客户端IP地址* @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字* @param boolean $adv 是否进行高级模式获取(有可能被 ...

  5. Service获取客户端IP地址(java)

    Java代码 <strong>1.基于jax-ws开发的webservice获取客户端ip的方法[在代理类XXXDelegate添加如下代码]:</strong> import ...

  6. java获取客户端IP地址工具类

    代码如下自取: import lombok.extern.slf4j.Slf4j;import javax.servlet.http.HttpServletRequest; import java.n ...

  7. 获取客户端IP地址定位城市信息

    获取客户端IP地址定位城市信息 1.首先获取客户端的IP地址 function getIPaddress(){ $IPaddress=''; if (isset($_SERVER)){ if (iss ...

  8. 使用搜狐接口获取客户端IP地址

    <!doctype html> <html> <head><meta charset="utf-8"><title>使用 ...

  9. JS 使用搜狐接口获取客户端IP地址

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  10. php $_server[remote_addr];,PHP获取客户端IP地址的方法$_SERVER['REMOTE_ADDR']

    /** * 获取客户端IP地址 * @param integer $type 返回类型 0 返回IP地址 1 返回IPV4地址数字 * @param boolean $adv 是否进行高级模式获取(有 ...

最新文章

  1. 记-curl post json数据,服务器后端$_POST接收不到数据
  2. P2421 A-B数对(增强版)
  3. linux用户登录失败,锁定用户
  4. solr(六): 集群
  5. deepin下安装QT
  6. 多元函数的泰勒展开公式
  7. Oracle导出表结构(含表名、字段名、数据类型、是否为空、字段说明)至Excel(简单详细)sql语句的形式
  8. windows10 插入耳机无弹出,无声音,提示扬声器、耳机未插入
  9. C# 扫描并读取图片中的文字(.NET Core)
  10. mysql多字段修改update_MySQL学习从这里出发!
  11. PPT无法打开,打开后总是修复的解决方法
  12. 计算机系统文件命名规则,电脑新建文件夹等的默认命名如何修改?[多图]
  13. android监听耳机,Android监听耳机按键事件
  14. python-列表元素绝对值排序
  15. OFFICES当出现“向程序发送命令时出现问题”错误怎么解决
  16. 批量nii文件转换为png图像
  17. 小恐龙游戏制作挑战:第8天-确定图片如何制作
  18. python——线性回归实例实战
  19. 华为,将智能计算进行到底
  20. 华为9306查看光功率

热门文章

  1. python工具包: argparse
  2. 2014浙大ACM网络省赛-----Talented Chef问题(Problem -C)
  3. Mac盖上屏幕后外接屏幕持续黑画面的解决方法
  4. macos big sur更新后,这些小组件原来这么好用
  5. Mac上翻译不在 Safari 中网站的方法
  6. 用EasyRecovery恢复手残误删的文件
  7. 7-2 求最大值及其下标编程总结
  8. 永久修改MySQL字符集(适用Mysql5.5、Mysql5.6、Mysql5.7以上)
  9. JQuery语法,选择器和事件
  10. codis-server 集群安装及部署