java nginx等代理或网关转发请求后获取客户端的ip地址,原理
网页默认是进行http连接了,http协议即超文本传送协议(Hypertext Transfer Protocol ),是工作TCP协议之上的协议
tcp连接需要三次握手,也就是调用底层的socket进行连接确认。而socket连接需要知道通信双方的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.
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;
}
继续使用上面数据
在有多个代理服务器的情况下,会将代理服务器的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地址,原理的更多相关文章
- Java 获取客户端真实IP地址
本文基于方法 HttpServletRequest.getHeader 和 HttpServletRequest.getRemoteAddr 介绍如何在服务器端获取客户端真实IP地址. 业务背景 服务 ...
- Java获取客户端真实IP地址的两种方法
在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实I ...
- java 获取客户端的ip地址
import javax.servlet.http.HttpServletRequest; import java.net.InetAddress; import java.net.UnknownHo ...
- Java获取客户端真实IP地址
Java代码 import javax.servlet.http.HttpServletRequest; /** * 获取对象的IP地址等信息 */ public class IPUtil { /** ...
- Nginx反向代理后,java获取客户端真实IP地址
一般情况下,java获取客户端IP地址的方法为request.getRemoteAddr();但这只是在没有网关或者代理的情况下,如果客户端将请求发送到nginx,再由nginx进行反向代理到目标服务 ...
- Envoy:经过envoy代理后获取客户端真实IP
在envoy作为前端代理时,用户ip的获取很重要,一般获取ip的方式.都是通过Header中的 X-Forward-For. X-Real-IP或 Remote addr 等属性获取,但是如果确保En ...
- 获取客户端真实ip地址(无视代理)
/// <summary> /// 获取客户端IP地址(无视代理) /// </summary> /// <returns>若失败则返回回送地址</retur ...
- nginx多层反向代理获取客户端真实ip
访问路径: 用户 --> www.chinasoft.cn(nginx反向代理) --> www.chinasoft.com(nginx反向代理) --> python服务端程序 经 ...
- Java正确获取客户端真实IP方法整理
在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实I ...
随机推荐
- Flask消息验证与提示
一,消息提示基本语法. 1,先新建一个Flask工作空间. 2,新建后自动得到一个app.py文件,直接运行可以看到基本效果.然后引入 from flask import flash.使用这个flas ...
- centos7 安装 oh my zsh
和在ubuntu 下安装十分相似(基本没区别) 安装zsh yum install zsh 改变系统bash chsh -s /bin/zsh git clone oh my zsh 项目: git@ ...
- PHP如何搭建百度Ueditor富文本编辑器
本文为大家分享了PHP搭建百度Ueditor富文本编辑器的方法,供大家参考,具体内容如下 下载UEditor 官网:下载地址 将下载好的文件解压到thinkphp项目中,本文是解压到PUBLIC目录下 ...
- TypeScript 函数-重载
function attr(name:string):string; function attr(age:number):string; function attr(nameorage:any):an ...
- 如何使用$.each()与$().each()以及他们的区别
1.首先,说下$.each(Arry/Object,function(index,val){ //index表示下标,val表示下标对应的值 }) 下面是使用$.each()的几种类型,其中arr2与 ...
- ajax(2)
AJAX全称: Asynchronous JavaScript and XML ( 异步的JavaScript 和 XML) Ajax的本质就是:XMLHttpRequest 对象: 案例: v ...
- NOIP-火柴棒等式
题目描述 给你n根火柴棍,你可以拼出多少个形如“A+B=C”的等式?等式中的A.B.C是用火柴棍拼出的整数(若该数非零,则最高位不能是0).用火柴棍拼数字0-9的拼法如图所示: 注意: 1. 加号与等 ...
- (转)基因芯片数据GO和KEGG功能分析
随着人类基因组计划(Human Genome Project)即全部核苷酸测序的即将完成,人类基因组研究的重心逐渐进入后基因组时代(Postgenome Era),向基因的功能及基因的多样性倾斜.通过 ...
- 1.Git安装
1.安装 首先下载安装包https://git-scm.com/downloads/ 双击安装任意盘符,双击之后一路Next,当然也可以修改默认配置 安装结束!
- IntelliJ IDEA配置Tomcat和Lombok
Tomcat的安装和配置 Tomcat 是在SUN公司的JSWDK(JavaServer Web DevelopmentKit)的基础上发展而来的一个优秀的Servlet容器,其本身完全是由Java编 ...