标签:tomcat x-forwarded-for glassfish
客户端发送请求到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; }
本文出自 “暗夜” 博客,请务必保留此出处http://icyore.blog.51cto.com/8486958/1701322
标签:tomcat x-forwarded-for glassfish
原文地址:http://icyore.blog.51cto.com/8486958/1701322