Java面试-如何获取客户端真实IP
在进行一些小游戏开发时,我们经常比较关注的一个功能便是分享。针对分享,我们希望能根据各个城市或者地区,能有不同的分享文案,辨识地区的功能如果由服务器来完成的话,我们就需要知道客户端的真实IP。今天我们就来看看服务器是如何获取到客户端的真实IP的。
nginx配置
首先,一个请求肯定是可以分为请求头和请求体的,而我们客户端的IP地址信息一般都是存储在请求头里的。如果你的服务器有用Nginx做负载均衡的话,你需要在你的location里面配置X-Real-IP
和X-Forwarded-For
请求头:
location ^~ /your-service/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://localhost:60000/your-service/;
}
X-Real-IP
在《实战nginx》中,有这么一句话:
经过反向代理后,由于在客户端和web服务器之间增加了中间层,因此web服务器无法直接拿到客户端的ip,通过$remote_addr变量拿到的将是反向代理服务器的ip地址。
这句话的意思是说,当你使用了nginx反向服务器后,在web端使用request.getRemoteAddr()
(本质上就是获取$remote_addr
),取得的是nginx的地址,即$remote_addr
变量中封装的是nginx的地址,当然是没法获得用户的真实ip的。但是,nginx是可以获得用户的真实ip的,也就是说nginx使用$remote_addr
变量时获得的是用户的真实ip,如果我们想要在web端获得用户的真实ip,就必须在nginx里作一个赋值操作,即我在上面的配置:
proxy_set_header X-Real-IP $remote_addr;
X-Forwarded-For
X-Forwarded-For
变量,这是一个squid开发的,用于识别通过HTTP代理或负载平衡器原始IP一个连接到Web服务器的客户机地址的非rfc标准,如果有做X-Forwarded-For
设置的话,每次经过proxy转发都会有记录,格式就是client1,proxy1,proxy2
以逗号隔开各个地址,由于它是非rfc标准,所以默认是没有的,需要强制添加。在默认情况下经过proxy转发的请求,在后端看来远程地址都是proxy端的ip 。也就是说在默认情况下我们使用request.getAttribute("X-Forwarded-For")
获取不到用户的ip,如果我们想要通过这个变量获得用户的ip,我们需要自己在nginx添加配置:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
意思是增加一个$proxy_add_x_forwarded_for
到X-Forwarded-For
里去,注意是增加,而不是覆盖,当然由于默认的X-Forwarded-For
值是空的,所以我们总感觉X-Forwarded-For
的值就等于$proxy_add_x_forwarded_for
的值,实际上当你搭建两台nginx在不同的ip上,并且都使用了这段配置,那你会发现在web服务器端通过request.getAttribute("X-Forwarded-For")
获得的将会是客户端ip和第一台nginx的ip。
那么$proxy_add_x_forwarded_for
又是什么?
$proxy_add_x_forwarded_for
变量包含客户端请求头中的X-Forwarded-For
与$remote_addr
两部分,他们之间用逗号分开。
举个例子,有一个web应用,在它之前通过了两个nginx转发,www.linuxidc.com
即用户访问该web通过两台nginx。
在第一台nginx中,使用:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
现在的$proxy_add_x_forwarded_for
变量的X-Forwarded-For
部分是空的,所以只有$remote_addr
,而$remote_addr
的值是用户的ip,于是赋值以后,X-Forwarded-For
变量的值就是用户的真实的ip地址了。
到了第二台nginx,使用:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
现在的$proxy_add_x_forwarded_for
变量,X-Forwarded-For
部分包含的是用户的真实ip,$remote_addr
部分的值是上一台nginx的ip地址,于是通过这个赋值以后现在的X-Forwarded-For
的值就变成了“用户的真实ip,第一台nginx的ip”,这样就清楚了吧。
服务器获取真实IP
代码为:
public static String getIpAddress(HttpServletRequest request) {
String Xip = request.getHeader("X-Real-IP");
String XFor = request.getHeader("X-Forwarded-For");
if (!Strings.isNullOrEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)) {
//多次反向代理后会有多个ip值,第一个ip才是真实ip
int index = XFor.indexOf(",");
if (index != -1) {
return XFor.substring(0, index);
} else {
return XFor;
}
}
XFor = Xip;
if (!Strings.isNullOrEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)) {
return XFor;
}
if (Strings.nullToEmpty(XFor).trim().isEmpty() || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("Proxy-Client-IP");
}
if (Strings.nullToEmpty(XFor).trim().isEmpty() || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("WL-Proxy-Client-IP");
}
if (Strings.nullToEmpty(XFor).trim().isEmpty() || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("HTTP_CLIENT_IP");
}
if (Strings.nullToEmpty(XFor).trim().isEmpty() || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (Strings.nullToEmpty(XFor).trim().isEmpty() || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getRemoteAddr();
}
return XFor;
}
我们来看看各个请求头的含义
X-Real-IP
nginx代理一般会加上此请求头。
X-FORWARDED-FOR
这是一个Squid
开发的字段,只有在通过了HTTP代理或者负载均衡服务器时才会添加该项。
Proxy-Client-IP 和 WL-Proxy-Client-IP
这个一般是经过apache http服务器的请求才会有,用apache http做代理时一般会加上Proxy-Client-IP
请求头,而WL-Proxy-Client-IP
是它的weblogic插件加上的头。
HTTP_CLIENT_IP
有些代理服务器会加上此请求头。在网上搜了一下,有一个说法是:
这是普通的 http header,伪造起来很容易,不要轻易信任用户输入。
curl -H 'client-ip: 8.8.8.8' lidian.club/phpinfo.php | grep _SERVER
你就能看到 _SERVER["HTTP_CLIENT_IP"] 了。
client-ip 和 client-host 是在 NAPT 还没普及的年代,企业内网假设的 http 透明代理,传给服务器的 header,只有极少数厂家用过,从来不是标准,也从来没成为过事实标准。
(大家最熟悉的事实标准就是 x-forwarded-for)
后来出现的 web proxy 也没见用过这个 header。
TCP/IP Illustrated Vol 3 没有讲过这个 header,网上的传言不可信。
可考的最早痕迹出现在2005年,日本一部 Perl/CGI 秘籍(9784798010779,270页)通过 client-ip 与 via 两个 header 屏蔽代理用户访问。
HTTP_X_FORWARDED_FOR
简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP,只有在通过了HTTP 代理(比如APACHE代理)或者负载均衡服务器时才会添加该项。它不是RFC中定义的标准请求头信息,在squid缓存代理服务器开发文档中可以找到该项的详细介绍。如果有该条信息, 说明您使用了代理服务器,地址就是后面的数值。可以伪造。标准格式如下:X-Forwarded-For: client1, proxy1, proxy2
总结
以上就是我在处理客户端真实IP的方法,如果你有什么意见或者建议,可以在下方留言。
有兴趣的话可以关注我的公众号或者头条号,说不定会有意外的惊喜。
Java面试-如何获取客户端真实IP的更多相关文章
- Java Web 获取客户端真实IP
Java Web 获取客户端真实IP 发生的场景:服务器端接收客户端请求的时候,一般需要进行签名验证,客户端IP限定等情况,在进行客户端IP限定的时候,需要首先获取该真实的IP.一般分为两种情况: 方 ...
- Java 获取客户端真实IP地址
本文基于方法 HttpServletRequest.getHeader 和 HttpServletRequest.getRemoteAddr 介绍如何在服务器端获取客户端真实IP地址. 业务背景 服务 ...
- 获取客户端真实IP地址
Java-Web获取客户端真实IP: 发生的场景:服务器端接收客户端请求的时候,一般需要进行签名验证,客户端IP限定等情况,在进行客户端IP限定的时候,需要首先获取该真实的IP. 一般分为两种情况: ...
- Nginx反向代理后应用程序获取客户端真实IP
Nginx反向代理后,Servlet应用通过request.getRemoteAddr()取到的IP是Nginx的IP地址,并非客户端真实IP,通过request.getRequestURL()获取的 ...
- 伪造IP及获取客户端真实IP地址
Fiddler支持自定义规则,可以实现对HTTP请求数据发送给Server前或HTTP应答数据发送给浏览器前进行修改.下面的例子将演示如何向所有HTTP请求数据中增加一个头.1)打开Fiddler,点 ...
- 获取客户端真实ip
// 获取客户端真实ip() protected function getIP() { global $ip; if (getenv("HTTP_CLIENT_IP")) $ip ...
- nginx 代理模式下,获取客户端真实IP
最近做博友推荐,发现个小问题,用$_SERVER['REMOTE_ADDR'];得到的都是服务器的地址192.168.96.52,搜索了一下,发现问题,改为$_SERVER['HTTP_X_REAL_ ...
- Kubernets中获取客户端真实IP总结
1. 导言 绝大多数业务场景都是需要知道客户端IP的 在k8s中运行的业务项目,如何获取到客户端真实IP? 本文总结了通行的2种方式 要答案的直接看方式一.方式二和总结 SEO 关键字 nginx i ...
- Tomcat 8.5中获取客户端真实IP及协议
获取客户端真实IP ServletRequest接口提供了getRemoteAddr方法用于获取客户端IP,但是当客户端通过代理服务器访问后端服务器的时候,服务器调用getRemoteAddr方法会返 ...
随机推荐
- (二十四)c#Winform自定义控件-单标题窗体
前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. 开源地址:https://gitee.com/kwwwvagaa/net_winform_custom_control ...
- 身体质量指数BMI
Solution: 方法一:"Python语言程序设计"(中国大学MOOC平台)的答案 分析:对比两种指标,将共性(相同的区间)和异性(不同的区间)细分.这样两种指标的判断条件(不 ...
- abp(net core)+easyui+efcore实现仓储管理系统——使用 WEBAPI实现CURD (十四)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- 在linux中部署项目并创建shell脚本
1.首先要在idea中父工程maven包下执行clean生成的target包 2.执行package打包,打包时候讲test勾去掉 3.将target包中生成的jar包cp出来 此处注意打包时必须要保 ...
- spring事务在实际项目开发中的使用
一, 事务的一些基础知识简单回顾一下,讲的不是很深入,网上博客很多. 1,关于事务的四大特性:原子性.隔离性.一致性.隔离性 本文不再赘述: 2,事务的隔离级别:读未提交,读已提交,可重复读,串行 ...
- 人脸识别开发套件RJ45、继电器、OTG、RS232接口说明
人脸识别开发套件RJ45.继电器.OTG.RS232接口说明 接口说明 D801A 人脸抓拍识别一体机是一款高性能.高可靠性的人脸识别类产品.依托深度学习算法扩展人脸库数量,准确率更高,支 ...
- FIS 插件机制
FIS 插件机制 author: @TiffanysBear 当我们使用 FIS 插件的时候,有没有想过自己也开发一个基于 FIS 的插件,参与 FIS 打包编译的整个流程:那么问题就来了: FIS ...
- Springboot源码分析之@Transactional
摘要: 对SpringBoot有多了解,其实就是看你对Spring Framework有多熟悉~ 比如SpringBoot大量的模块装配的设计模式,其实它属于Spring Framework提供的能力 ...
- java.lang.TypeNotPresentException: Type javax.xml.bind.JAXBContext not present解决方案
因为JAXB-API是java ee的一部分,在jdk9中没有在默认的类路径中: java ee api在jdk中还是存在的,默认没有加载而已,jdk9中引入了模块的概念,可以使用 模块命令--add ...
- 使用 Docker Compose 快速构建 TiDB 集群
本文档介绍如何在单机上通过 Docker Compose 快速一键部署一套 TiDB 测试集群.Docker Compose 可以通过一个 YAML 文件定义多个容器的应用服务,然后一键启动或停止. ...