【Glassfish调查】获取客户端Addr和Host

客户端发送请求到GF时通过gorouter代理转发,转发时gorouter会修改header中的remoteAddr和remoteHost的值,所以在GF中获得的remoteAddr和remoteHost的值为gorouter的remoteAddr和remoteHost的值,与标准式样不一致。

使用gorouter转发请求时,它会将请求IP存储在头X-Forwarded-For中,所以可以通过对X-Forwarded-For头的解析获得客户端IP。
X-Forwarded-For:简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP。这一HTTP头中X-Forwarded-For的格式如下:
X-Forwarded-For: client1, proxy1, proxy2
其中的IP通过一个逗号+空格把多个IP地址区分开, 最左边(client1)是最原始客户端的IP地址, 代理服务器每成功收到一个请求,就把请求来源IP地址添加到右边。这是在RFC7239规范中说明的,gorouter也遵守该规范。

设置Request的remotAddr和remoteHost属性有两个方案:
【方案1】
因为Request请求在通过代理时会将发送请求的IP存入到X-Forwarded-For头中且每次都会添加到末尾,所以取出X-Forwarded-For头的第一个IP地址,那么它就是客户端的IP。具体实现如下:
1.    判断X-Forwarded-For头是否存在且不为空。
2.    如果为空则不设置Request的remotAddr和remoteHost属性。
3.    如果X-Forwarded-For头存在且不为空,则取其第一个IP地址,并赋值给Request的remoteAddr和remoteHost属性。

示例:

X-Forwarded-For:Client_IP, proxy1, CF_IP, 192.168.0.10(192.168.0.10是内部地址)
Client_IP是X-Forwarded-For头中第一个IP,所以判断它就是客户端IP,将其赋值给Request的remoteAddr和remoteHost属性。

代码:

 private String getFirstIpOfXFF() {
        String remoteIp = null;
        StringBuilder concatRemoteIpHeaderValue = new StringBuilder();
        for (Iterator<String> headerIterator = getHeaders(remoteIpHeader)
                .iterator(); headerIterator.hasNext();) {
            if (concatRemoteIpHeaderValue.length() > 0) {
                concatRemoteIpHeaderValue.append(", ");
            }
            concatRemoteIpHeaderValue.append(headerIterator.next());
        }
        String[] remoteIpHeaderValue = commaDelimitedListToStringArray(concatRemoteIpHeaderValue
                .toString());
        if (remoteIpHeaderValue.length != 0) {
            remoteIp = remoteIpHeaderValue[0].trim();
        }
        return remoteIp;
    }

【方案2】
Tomcat的实现方式
因为Request请求在通过代理时会将发送请求的IP存入到X-Forwarded-For头中且每次都会添加到末尾,所以去除末尾属于代理服务器的IP,那么剩下的最后一个就是客户端的IP。具体实现如下:
1、
配置文件

<Valve
   className="org.apache.catalina.valves.RemoteIpValve"  
   internalProxies="192\.168\.0\.10, 192\.168\.0\.11"  
   remoteIpHeader="x-forwarded-for"  
   remoteIpProxiesHeader="x-forwarded-by"  
   trustedProxies="192\.168\.0\.13, 192\.168\.0\.12"/>

2、
如果X-Forwarded-For头存在且不为空,从其中读取IP列表,按从右向左的顺序扫描各个IP,规则如下:
1)如果IP列表中当前的IP与internalProxies中的IP匹配,该IP被删去,处理下个IP。
2)如果IP列表中当前的IP与truestedProxies中的IP匹配,该IP被添加在proxiesHeaderValue中,处理下个IP。
如果IP列表中当前的IP与truestedProxies中的IP不匹配,该IP就被置为客户端IP。
3)将IP列表中剩余IP添加到newRemoteIpHeaderValue,扫描结束。
3、
如果扫描结束还没有找到符合规则的客户端IP,则将最后扫描的IP设置为客户端IP。
4、
将上述扫描中产生的客户端IP赋值给Request的remoteAddr和remoteHost属性。
将上述扫描中产生的proxiesHeaderValue中的IP设置到remoteIpProxiesHeader头。
将上述循环中产生的newRemoteIpHeaderValue中的IP设置到remoteIpHeader头。

如图:


示例:
X-Forwarded-For:Client_IP, ELB_IP, CF_IP, 192.168.0.10(192.168.0.10是内部地址)
配置文件

<Valve   
   className="org.apache.catalina.valves.RemoteIpValve"  
   internalProxies="192\.168\.0\.10"  
   remoteIpHeader="x-forwarded-for"  
   remoteIpProxiesHeader="x-forwarded-by"  
   trustedProxies="CF_IP"/>

获取客户端IP过程如下:
1、X-Forwarded-For头不为空,获取IP列表Client_IP, ELB_IP, CF_IP, 192.168.0.10。
2、获取到192.168.0.10,与internalProxies中IP匹配,匹配成功,删除此IP然后获取下一个IP。
3、获取到CF_IP,与internalProxies中IP匹配,匹配失败,与truestedProxies中的IP匹配,匹配成功,添加此IP到proxiesHeaderValue然后获取下一个IP。
4、获取到ELB_IP,与internalProxies中IP匹配,匹配失败,与truestedProxies中的IP匹配,匹配失败,所以ELB_IP就是客户端IP。
5、将ELB_IP赋值给Request的remoteAddr和remoteHost属性。
6、将CF_IP设置到remoteIpProxiesHeader头。
7、将Client_IP设置到remoteIpHeader头。

代码:

private HashMap<String, LinkedList<String>> getMatchedIpOfXFF() {
        String remoteIp = null;
        LinkedList<String> remoteIpValue = new LinkedList<String>();
        LinkedList<String> proxiesHeaderValue = new LinkedList<String>();
        LinkedList<String> newRemoteIpHeaderValue = new LinkedList<String>();
        HashMap<String, LinkedList<String>> headerValueMap = new HashMap<String, LinkedList<String>>();
        StringBuilder concatRemoteIpHeaderValue = new StringBuilder();
        for (Iterator<String> headerIterator = getHeaders(remoteIpHeader)
                .iterator(); headerIterator.hasNext();) {
            if (concatRemoteIpHeaderValue.length() > 0) {
                concatRemoteIpHeaderValue.append(", ");
            }
            concatRemoteIpHeaderValue.append(headerIterator.next());
        }
        String[] remoteIpHeaderValue = commaDelimitedListToStringArray(concatRemoteIpHeaderValue
                .toString());
        int idx;
        for (idx = remoteIpHeaderValue.length - 1; idx >= 0; idx--) {
            String currentRemoteIp = remoteIpHeaderValue[idx];
            remoteIp = currentRemoteIp;
            if (internalProxies.matcher(currentRemoteIp).matches()) {
                // do nothing, ignore internal proxies.
            } else if (trustedProxies != null
                    && trustedProxies.matcher(currentRemoteIp).matches()) {
                // if currentRemoteIp match with trusted proxies,add this ip to
                // proxiesHeader.
                proxiesHeaderValue.addFirst(currentRemoteIp);
            } else {
                idx--; // decrement idx because break statement doesn‘t do it.
                break;
            }
        }
        // continue to loop on remoteIpHeaderValue to build the new value of the
        // remoteIpHeader.
        for (; idx >= 0; idx--) {
            String currentRemoteIp = remoteIpHeaderValue[idx];
            newRemoteIpHeaderValue.addFirst(currentRemoteIp);
        }
        remoteIpValue.addFirst(remoteIp);
        headerValueMap.put("remoteIpValue", remoteIpValue);
        headerValueMap.put("proxiesHeaderValue", proxiesHeaderValue);
        headerValueMap.put("newRemoteIpHeaderValue", newRemoteIpHeaderValue);
        return headerValueMap;
    }
时间: 2024-07-28 19:52:48

【Glassfish调查】获取客户端Addr和Host的相关文章

Java获取客户端IP

在开发工作中,我们常常需要获取客户端的IP.一般获取客户端的IP地址的方法是:request.getRemoteAddr();但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实IP地址了. 原因:由于在客户端和服务之间增加了中间代理,因此服务器无法直接拿到客户端的IP,服务器端应用也无法直接通过转发请求的地址返回给客户端. 现在图示代理上网和IP的关系: 第一种情况:不通过代理上网,服务器端拿到真实IP 第二种情况:通过代理服务器如:Nginx,Squid等一层代理或多层

ASP.NET获取客户端及服务器的信息

客户端信息: 1. 在ASP.NET中专用属性: 获取服务器电脑名:Page.Server.ManchineName 获取用户信息:Page.User 获取客户端电脑名:Page.Request.UserHostName 获取客户端电脑IP:Page.Request.UserHostAddress 2. 在网络编程中的通用方法: 获取当前电脑名:static System.Net.Dns.GetHostName() 根据电脑名取出全部IP地址:static System.Net.Dns.Reso

根据Request获取客户端IP 内网IP及外网IP

在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr() ,这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实IP地址了.如果使用了反向代理软件,将http://192.168.1.110:2046/ 的URL反向代理为http://www.xxx.com/ 的URL时,用request.getRemoteAddr() 方法获取的IP地址是:127.0.0.1 或 192.168.1.110 ,而并不是客户

ASP.NET获取客户端信息,获取客户端IP等等

山上明月 ASP.NET能知道的东西 获取服务器电脑名: Page.Server.ManchineName 获取用户信息: Page.User 获取客户端电脑名:Page.Request.UserHostName 获取客户端电脑IP: Page.Request.UserHostAddress 1. 在ASP.NET中专用属性: 获取服务器电脑名:Page.Server.ManchineName 获取用户信息:Page.User 获取客户端电脑名:Page.Request.UserHostName

ASP.NET获取客户端、服务器端的信息

ASP.NET获取客户端.服务器端基础信息 1. 在ASP.NET中专用属性: 获取服务器电脑名:Page.Server.ManchineName 获取用户信息:Page.User 获取客户端电脑名:Page.Request.UserHostName 获取客户端电脑IP:Page.Request.UserHostAddress 2. 在网络编程中的通用方法: 获取当前电脑名:static System.Net.Dns.GetHostName() 根据电脑名取出全部IP地址:static Syst

nginx反向代理node.js获取客户端IP

使用Nginx做node.js程序的反向代理,会有这么一个问题:在程序中获取的客户端IP永远是127.0.0.1 如果想要拿到真实的客户端IP改怎么办呢? 一.首先配置Nginx的反向代理 proxy_set_header server { listen 80; server_name chat.luckybing.top; location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; pro

nodejs 获取客户端 ip 地址

为什么要获取客户端 ip: php:我们需要拿到用户客户端的ip信息,以识别用户位置,但现在我们拿到的地址永远是杭州 前端:我查一下,稍等 .... 明白了,我们加了一层 node 服务器,服务器在杭州,你们拿到的是 node 服务器的 ip php:那怎么办? 前端:我给你加一个 ip 字段吧 php:哦~ 前端:(噼里啪啦噼里啪啦......) 好了,你们看一下有值了吗? php:有了,但值不对,你给我传的是 ::ffff:127.0.0.1 ,前边那一堆 f 是什么鬼,去掉,我们只要后边的

nginx代理,tomcat部署服务器,后端获取客户端真实ip

1.环境部署说明 后端部署在tomcat服务器上,前端用nginx做代理访问 tomcat部署目录 nginx配置: upstream wcfront{     server  localhost:8991;//后台接口 } server {     listen       8998;//h5访问接口     server_name  192.168.2.37;     charset utf-8;     proxy_set_header Host $host:$server_port;  

.Net Core/Framework之Nginx反向代理后获取客户端IP等数据探索

原文:.Net Core/Framework之Nginx反向代理后获取客户端IP等数据探索 公司项目最近出现获取访问域名.端口.IP错误现象,通过排查发现, 之前项目一直通过Nginx自定义Headers信息来获取,但最近运维人员失误操作造成自定义Header信息丢失,造成项目拿不到对应的数据.思前想后,想找找官方有没有关于此类问题通用标准化的解决方案. 一.Nginx配置如下: proxy_redirect off; proxy_set_header Host $host; proxy_set