一般在单机版的tomcat获取请求来源的ip,可以使用下面方式这么干,下文的request是HttpServletRequest对象。
String remoteAddr = request.getRemoteAddr();
但是如果了使用反向代理例如niginx,由于对客户请求的代理,此时request.getRemoteAddr()
获取要么是本级地址
127.0.0.1
要么就是192.168.x.x
这个内网地址,因为这种获取Ip的方式仅限与客户端和服务器端直接通信的方式才有效。在知乎上看到了一个很形象的反向代理图,如下所示:
反向代理对服务器做了代理,客户端是和代理进行通信。所以这种获取ip地址的信息获取到的是内网ip地址。
在nginx反向代理中,在nginx.conf 我们常常可以看到如下配置:X-Real-IP
,X-Forwarded-For
.
location / {
root html;
index index.html index.htm index.jsp;
#proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://192.168.1.224;
}
由于客户端和服务器端中间有个代理层的存在,所以服务器无法直接拿到客户真实的ip地址,在代理转发客户请求的时候,请求头(即Header)增加了X-FORWARDED-FOR
这个属性,这个属性是追踪原来客户端和服务器端的IP地址。只有通过了Http代理或者负载均衡服务器才会有这个属性。当客户端发起请求时,将会有以下5步操作。
dns域名解析,通过域名找到ip
代理服务器的TCP监听端口,nginx默认80端口,当有TCP连接后,将客户端请求经过处理发送到服务器
服务器对接收到的Http数据包进行解压,解密,创建会话并处理请求
服务器处理和相应请求后,将相应转发给代理服务器
代理服务器将服务端的响应在转发给客户端服务器
所以整个请求中,客户端并没有和服务器端直接进行通信,所以request.getRemoteAddr()
获取的是代理服务器的地址,因为服务器接收的是代理服务器发起的请求。
所以可以用下面方式获取客户端IP地址
/**
* 获取IP地址
*
*/
public static String getIpAddr(HttpServletRequest request)
{
String ip = request.getHeader("X-Real-IP");
if(!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip))
return ip;
ip = request.getHeader("X-Forwarded-For");
if(!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip))
{
int index = ip.indexOf(',');
if(index != -1)
return ip.substring(0, index);
else
return ip;
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
ip = request.getHeader("Proxy-Client-IP");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
ip = request.getHeader("WL-Proxy-Client-IP");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
ip = request.getHeader("HTTP_CLIENT_IP");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
ip = request.getRemoteAddr();
return ip;
}
String ip = request.getHeader("X-Real-IP");
X-Real-IP
是Nginx用来获取用户的真实ip地址,但是有时候可能获取不到返回是UNKONWN。所以当获取不到使用下面方式进行获取:
String ip = request.getHeader("X-Forwarded-For");
X-Forwarded-For
有时候可能获取不到,因为服务器不一定使用了反向代理,所以可能返回的也是UNKONWN,获取不到可以使用以下方式进行获取:
String ip = request.getHeader("Proxy-Client-IP");
Proxy-Client-IP
是apache服务器定制一个请求头,这个属性只在apache服务器才可以看到。所以当服务器不是apache服务器,也会返回UNKONWN
String ip = request.getHeader("WL-Proxy-Client-IP");
WL- Proxy-Client-IP
其中WL是weblogic的缩写,这通常用户weblogic服务器环境中,也可能返回UNKONWN
String ip = request.getHeader("HTTP_CLIENT_IP");
HTTP_CLIENT_IP
有些代理服务器会加上此配置
String ip = request.getHeader("HTTP_X_FORWARD_FOR");
HTTP_X_FORWARD_FOR
可以在nginx配置此项,一般为 HTTP_X_FORWARD_FOR $remote_addr;
看了上面的相关头解释和获取IP的方法,则可以将其集成到我们的项目中,如下采用webService接口获取IP和基于Spring MVC方式获取IP
下面以scala定一个webService接口
@Path("/webService/getIp")
@POST
def getClientIp(@Context httpServletRequest: HttpServletRequest): String
实现类:
// 获取客户端的ip
override def getClientIp(@Context httpServletRequest: HttpServletRequest): String = {
var ip =""
Try({
ip = StringUtils.defaultIfBlank(getRemoteIP(httpServletRequest), "unknown")
println("获取ip="+ip)
}) match {
case Success(_) => ip
case Failure(e) => {
throw e
}
}
}
@PostMapping(Array("/springMvc/getIp"))
def getIp(httpRequest: HttpServletRequest): String = {
var ip =""
Try({
ip = StringUtils.defaultIfBlank(getRemoteIP(httpServletRequest), "unknown")
println("获取ip="+ip)
}) match {
case Success(response) => ip
case Failure(e) => throw e
}
}
所以使用SpringMvc方式获取ip还是webService可以使用上面的案列,非常简单。