客户端发送请求到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
 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、
配置文件

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是内部地址)
配置文件

获取客户端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
> getMatchedIpOfXFF() {        String remoteIp = null;        LinkedList
 remoteIpValue = new LinkedList
();        LinkedList
 proxiesHeaderValue = new LinkedList
();        LinkedList
 newRemoteIpHeaderValue = new LinkedList
();        HashMap
> headerValueMap = new HashMap
>();        StringBuilder concatRemoteIpHeaderValue = new StringBuilder();        for (Iterator
 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;    }