在web开发时有时需要验证用户的真实IP,在Java开发中,使用request.getRemoteAddr()
得到的可能是request路经的代理(Proxy)或者负载平衡器(Load Balancer)的IP,而并非用户的真实IP。一般地,Proxy或者Load Balancer在转发一个请求时,会将原有的IP加在http request的X-FORWARDED-FOR header中,如果该header已经存在,proxy会将收到的IP添加到已有的value中,比如
X-FORWARDED-FOR: 10.4.16.159, 10.4.16.180
如果每个Proxy或Load Balancer都把它们收到的原始IP添加到X-FORWARDED-FOR header中,request的原始IP就是10.4.16.159,而不是request.getRemoteAddr()
得到的IP值。
无疑得到Proxy或者Load Balancer的IP是毫无意义的,那么怎么得到用户的原始IP呢?本文总结出四种方法:
使用前三种方式,都可以使开发者调用request.getRemoteAddr() 就能得到用户的IP。本着代码应主要服务于业务逻辑的原则,推荐尽可能使用前两种方式。第三中方式次之,第四种是特殊需求下的无奈选择了。
四种方法具体用法简要介绍如下:
配置Apache mod_remoteip模块
该模块发布在Apache v2.3,启用该模块后,配置如下指令:
RemoteIPHeader X-Forwarded-By
RemoteIPTrustedProxy 10.0.2.16/28
RemoteIPTrustedProxy proxy.example.com
表示该将从X-Forwarded-For Header中提取用户的真实IP。该Header里可能包含多个IP或者域名,比如:
X-FORWARDED-FOR: 10.4.16.159, 10.4.16.180,proxy.example.com
该模块会对Header的值从右向左扫描,如果遇到因为proxy.example.com是可信代理,10.4.16.180不可信,那么该module会把10.4.16.180值为用户真实IP。如果所有的IP都可信,10.4.16.159将被值为用户真实IP。
注:如果不配置RemoteIPTrustedProxy,则所有代理都是可信的。
配置Tomcat RemoteIpValve组件
在server.xml里添加如下配置:
与Apache remoteip模块类似,该配置会使Tomcat从x-forwarded-for header中读取IP列表,按从左向右的顺序扫描各个IP,规则如下:
注:internalProxies,trustedProxies的值为正则表达式,如果信任所有IP,可值为“.*”
通过Application级别的Filter(比如XForwardedFilter)
该方式在web application中添加Filter,使用Filter获取x-forwarded-for Header中,按照自己的策略从中选择可能的用户IP。XForwardedFilter是比较常用的实现,将被集成在Apache Tomcat 7中,配置方式与一般的Filter相同,可配置的属性的用法也与Tomcat RemoteIpValve相同。代码示例如下:
XForwardedFilter
fr.xebia.servlet.filter.XForwardedFilter
allowedInternalProxies 192\.168\.0\.10, 192\.168\.0\.11
remoteIPHeader x-forwarded-for
remoteIPProxiesHeader x-forwarded-by
trustedProxies proxy1, proxy2
XForwardedFilter
/*
REQUEST
通过Application代码从Request Header中获取
最不推荐的一种方式,把业务逻辑无关的代码侵入到应用代码中去。通过判断x-forwarded-for Header是否存在,并且根据自身对可信代理及内部代理的策略,参考以上解释的常规做法,自己实现吧。