在没有网关或者反向代理软件情况下,java里获取客户端ip地址的方法是request.getRemoteAddr()
先解释下http协议和TCP协议:
网页默认是进行http连接了,http协议即超文本传送协议(Hypertext Transfer Protocol ),是工作TCP协议之上的协议
tcp连接需要三次握手,也就是调用底层的socket进行连接确认。而socket连接需要知道通信双方的ip地址和端口才可以进行数据的正确传输。
由上面可以知道request.getRemoteAddr()方法其实就是获取的连接的客户端socket的ip地址。
 
但如果我们客户端将要发送接口请求先发送到一台代理请求服务器或者网关后,再由他们进行数据请求,
这时我们使用上面的getRemoteAddr()方法获取到了ip地址就是代理请求服务器或网关的ip地址。
而不是真实的客户端ip地址
也就是说通过nginx等反向代理软件时,不能通过这个方法获取客户端真是ip地址。
 
如真实ip:223.104.1. 广东省广州市 移动
访问域名www.zwh.com
nginx所在服务器193.112.28.
这个域名和nginx所在服务器绑定,
客户端访问时,如www.zwh.com/项目名/index.jsp
这个访问了是nginx的80端口,然后nginx再将这个请求重定向到localhost:/项目名/index.jsp
而nginx在将请求进行重定向时会在请求头header中增加一个x-forwarded-for信息,用以跟踪原有客户端ip地址和原来客户端请求的服务器地址
存放的就是x-forwarded-for:223.104.1.240 对应java方法来说,request.getRemoteAddr(),因为直接请求的客户端是nginx,在同一服务器上,所以这个方法获取到的ip地址是127.0.0.1或者193.112.28.
 
要获取真实ip地址就要使用request.getHeader("x-forwarded-for")获取请求头部真实ip地址
,但是当xxx.conf配置server节点时,forwarded_for设置为off,则X-Forwarded-For:unknown
默认forwarded_for是on
 
先看下nginx的部分配置
location / {
proxy_redirect off;
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-real-ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

继续使用上面数据

这个$host 其实是nginx服务器本身的ip地址:193.112.28.110
 $remote_addr这个地址是想nginx请求的客户端地址:223.104.1.240
 $proxy_add_x_forwarded_for 这个变量和remote_addr的区别是
proxy_add_x_forwarded_for会将经过的所有代理ip相加起来,
remote_addr则只是获取到直接连接到nginx的ip地址
  在有多个代理服务器的情况下,会将代理服务器的X-Forwarded-For头部的ip地址和客户端的ip地址相加,
  即:X-Forwarded-For请求头部header的ip字符串 + 直接向nginx服务器发送请求的客户端地址
  换个表达请求的过程是client--->nginx1--->nginx2---->tomcat
  而假设client:ip1, nginx1:ip2, nginx2:ip3, tomcat:ip4
  那么请求request到tomcat时,
  请求头部header中的X-Forwarded-For信息是=ip1,ip2,ip3

简单了方法

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;
}

上面三个请求头,大部分的代理或者网关都会加上x-forwarded-for:clientip,proxy1ip,proxy2ip

Proxy-Client-IP这个一般是apache,http服务器才会有,经过apache代理时,apache会在请求头加上x-forwarded-for和Proxy-Client-IP

WL-Proxy-Client-IP这个一般是weblogic才会加上的头部

注意:

1、这些请求头都不是http协议里的标准请求头,也就是说这个是各个代理服务器自己规定的表示客户端地址的请求头。如果哪天有一个代理服务器软件用oooo-client-ip这个请求头代表客户端请求,那上面的代码就不行了。

2、这些请求头不是代理服务器一定会带上的,网络上的很多匿名代理就没有这些请求头,所以获取到的客户端ip不一定是真实的客户端ip。代理服务器一般都可以自定义请求头设置。

3、即使请求经过的代理都会按自己的规范附上代理请求头,上面的代码也不能确保获得的一定是客户端ip。不同的网络架构,判断请求头的顺序是不一样的。

4、最重要的一点,请求头都是可以伪造的。如果一些对客户端校验较严格的应用(比如投票)要获取客户端ip,应该直接使用ip = request.getRemoteAddr (),虽然获取到的可能是代理的ip而不是客户端的ip,但这个获取到的ip基本上是不可能伪造的,也就杜绝了刷票的可能。(有分析说arp欺骗+syn有可能伪造此ip,如果真的可以,这是所有基于TCP协议都存在的漏洞),这个ip是tcp连接里的ip。
链接

ip工具类

import javax.servlet.http.HttpServletRequest;
/**
* IP地址工具类
* @author xudongdong
*
*/
public class IpUtil { /**
* 私有化构造器
*/
private IpUtil() {
}
/**
* 获取真实IP地址
* <p>使用getRealIP代替该方法</p>
* @param request req
* @return ip
*/
@Deprecated
public static String getClinetIpByReq(HttpServletRequest request) {
// 获取客户端ip地址
String clientIp = request.getHeader("x-forwarded-for");
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getHeader("Proxy-Client-IP");
}
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getHeader("WL-Proxy-Client-IP");
}
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getRemoteAddr();
}
/*
* 对于获取到多ip的情况下,找到公网ip.
*/
String sIP = null;
if (clientIp != null && !clientIp.contains("unknown") && clientIp.indexOf(",") > 0) {
String[] ipsz = clientIp.split(",");
for (String anIpsz : ipsz) {
if (!isInnerIP(anIpsz.trim())) {
sIP = anIpsz.trim();
break;
}
}
/*
* 如果多ip都是内网ip,则取第一个ip.
*/
if (null == sIP) {
sIP = ipsz[0].trim();
}
clientIp = sIP;
}
if (clientIp != null && clientIp.contains("unknown")){
clientIp =clientIp.replaceAll("unknown,", "");
clientIp = clientIp.trim();
}
if ("".equals(clientIp) || null == clientIp){
clientIp = "127.0.0.1";
}
return clientIp;
} /**
* 判断IP是否是内网地址
* @param ipAddress ip地址
* @return 是否是内网地址
*/
public static boolean isInnerIP(String ipAddress) {
boolean isInnerIp;
long ipNum = getIpNum(ipAddress);
/**
私有IP:A类 10.0.0.0-10.255.255.255
B类 172.16.0.0-172.31.255.255
C类 192.168.0.0-192.168.255.255
当然,还有127这个网段是环回地址
**/
long aBegin = getIpNum("10.0.0.0");
long aEnd = getIpNum("10.255.255.255"); long bBegin = getIpNum("172.16.0.0");
long bEnd = getIpNum("172.31.255.255"); long cBegin = getIpNum("192.168.0.0");
long cEnd = getIpNum("192.168.255.255");
isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd)
|| ipAddress.equals("127.0.0.1");
return isInnerIp;
}
private static long getIpNum(String ipAddress) {
String[] ip = ipAddress.split("\\.");
long a = Integer.parseInt(ip[0]);
long b = Integer.parseInt(ip[1]);
long c = Integer.parseInt(ip[2]);
long d = Integer.parseInt(ip[3]);
return a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
} private static boolean isInner(long userIp, long begin, long end) {
return (userIp >= begin) && (userIp <= end);
}
public static String getRealIP(HttpServletRequest request){
// 获取客户端ip地址
String clientIp = request.getHeader("x-forwarded-for");
if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
clientIp = request.getRemoteAddr();
}
String[] clientIps = clientIp.split(",");
if(clientIps.length <= 1) return clientIp.trim();
// 判断是否来自CDN
if(isComefromCDN(request)){
if(clientIps.length>=2) return clientIps[clientIps.length-2].trim();
}
return clientIps[clientIps.length-1].trim();
}
private static boolean isComefromCDN(HttpServletRequest request) {
String host = request.getHeader("host");
return host.contains("www.189.cn") ||host.contains("shouji.189.cn") || host.contains(
"image2.chinatelecom-ec.com") || host.contains(
"image1.chinatelecom-ec.com");
}
}
 

java nginx等代理或网关转发请求后获取客户端的ip地址,原理的更多相关文章

  1. Java 获取客户端真实IP地址

    本文基于方法 HttpServletRequest.getHeader 和 HttpServletRequest.getRemoteAddr 介绍如何在服务器端获取客户端真实IP地址. 业务背景 服务 ...

  2. Java获取客户端真实IP地址的两种方法

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

  3. java 获取客户端的ip地址

    import javax.servlet.http.HttpServletRequest; import java.net.InetAddress; import java.net.UnknownHo ...

  4. Java获取客户端真实IP地址

    Java代码 import javax.servlet.http.HttpServletRequest; /** * 获取对象的IP地址等信息 */ public class IPUtil { /** ...

  5. Nginx反向代理后,java获取客户端真实IP地址

    一般情况下,java获取客户端IP地址的方法为request.getRemoteAddr();但这只是在没有网关或者代理的情况下,如果客户端将请求发送到nginx,再由nginx进行反向代理到目标服务 ...

  6. Envoy:经过envoy代理后获取客户端真实IP

    在envoy作为前端代理时,用户ip的获取很重要,一般获取ip的方式.都是通过Header中的 X-Forward-For. X-Real-IP或 Remote addr 等属性获取,但是如果确保En ...

  7. 获取客户端真实ip地址(无视代理)

    /// <summary> /// 获取客户端IP地址(无视代理) /// </summary> /// <returns>若失败则返回回送地址</retur ...

  8. nginx多层反向代理获取客户端真实ip

    访问路径: 用户 --> www.chinasoft.cn(nginx反向代理) --> www.chinasoft.com(nginx反向代理) --> python服务端程序 经 ...

  9. Java正确获取客户端真实IP方法整理

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

随机推荐

  1. Flask消息验证与提示

    一,消息提示基本语法. 1,先新建一个Flask工作空间. 2,新建后自动得到一个app.py文件,直接运行可以看到基本效果.然后引入 from flask import flash.使用这个flas ...

  2. centos7 安装 oh my zsh

    和在ubuntu 下安装十分相似(基本没区别) 安装zsh yum install zsh 改变系统bash chsh -s /bin/zsh git clone oh my zsh 项目: git@ ...

  3. PHP如何搭建百度Ueditor富文本编辑器

    本文为大家分享了PHP搭建百度Ueditor富文本编辑器的方法,供大家参考,具体内容如下 下载UEditor 官网:下载地址 将下载好的文件解压到thinkphp项目中,本文是解压到PUBLIC目录下 ...

  4. TypeScript 函数-重载

    function attr(name:string):string; function attr(age:number):string; function attr(nameorage:any):an ...

  5. 如何使用$.each()与$().each()以及他们的区别

    1.首先,说下$.each(Arry/Object,function(index,val){ //index表示下标,val表示下标对应的值 }) 下面是使用$.each()的几种类型,其中arr2与 ...

  6. ajax(2)

    AJAX全称: Asynchronous JavaScript and XML    ( 异步的JavaScript 和 XML) Ajax的本质就是:XMLHttpRequest 对象: 案例: v ...

  7. NOIP-火柴棒等式

    题目描述 给你n根火柴棍,你可以拼出多少个形如“A+B=C”的等式?等式中的A.B.C是用火柴棍拼出的整数(若该数非零,则最高位不能是0).用火柴棍拼数字0-9的拼法如图所示: 注意: 1. 加号与等 ...

  8. (转)基因芯片数据GO和KEGG功能分析

    随着人类基因组计划(Human Genome Project)即全部核苷酸测序的即将完成,人类基因组研究的重心逐渐进入后基因组时代(Postgenome Era),向基因的功能及基因的多样性倾斜.通过 ...

  9. 1.Git安装

    1.安装 首先下载安装包https://git-scm.com/downloads/ 双击安装任意盘符,双击之后一路Next,当然也可以修改默认配置 安装结束!

  10. IntelliJ IDEA配置Tomcat和Lombok

    Tomcat的安装和配置 Tomcat 是在SUN公司的JSWDK(JavaServer Web DevelopmentKit)的基础上发展而来的一个优秀的Servlet容器,其本身完全是由Java编 ...