Java后端在Nginx代理下获取请求的真实IP

### 问题
在Javaweb程序中,一般通过request.getRemoteAddr()获取请求源IP地址,但是在nginx反向代理后台服务后,该方法获取的IP将会变为nginx所在IP,一般为本机地址(127.0.0.1),那么如何解决这种情况呢?

### 解决
一般解决方法是nginx通过配置在请求头中保留客户真实请求IP,同时后端程序改造获取请求真实IP的方法,协同来解决

#### Nginx代理保留真实IP
> 一些参考
- [X-Forwarded-For与$proxy_add_x_forwarded_for](https://www.cnblogs.com/lyongerr/articles/4994157.html "X-Forwarded-For与$proxy_add_x_forwarded_for")  
- [Nginx配置说明](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#example "Nginx配置说明")


```
location /mineproxypass/ {  
	proxy_pass   http://localhost:10086/nookblog/; 
	
	#保留代理之前的host 默认值为 $proxy_host
	#proxy_set_header Host $host;
	#保留代理之前的真实客户端ip
	proxy_set_header X-Real-IP $remote_addr; 
	#在多级代理的情况下,记录每次代理之前的客户端真实ip
	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
	#指定修改被代理服务器返回的响应头中的location头域跟refresh头域数值
	#proxy_redirect             default ; 
	#default等效于下述配置,但是当设置proxy_set_header Host $host后,该配置失效	
	proxy_redirect   http://localhost:10086/nookblog/ /mineproxypass/;
	#cookie转发相关
	proxy_cookie_path  /nookblog /mineproxypass;
}
```
#### Java优化获取请求IP方法

```java
public static String ipAddr(HttpServletRequest request) {
	String ipAddress = request.getHeader("x-forwarded-for");
	if (StringX.isBlank(ipAddress) || UNKNOWN_MAGIC.equalsIgnoreCase(ipAddress)) {
		ipAddress = request.getHeader("Proxy-Client-IP");
	}
	if (StringX.isBlank(ipAddress) || UNKNOWN_MAGIC.equalsIgnoreCase(ipAddress)) {
		ipAddress = request.getHeader("WL-Proxy-Client-IP");
	}
	if (StringX.isBlank(ipAddress) || UNKNOWN_MAGIC.equalsIgnoreCase(ipAddress)) {
		ipAddress = request.getHeader("X-Real-IP");
	}
	if (StringX.isBlank(ipAddress) || UNKNOWN_MAGIC.equalsIgnoreCase(ipAddress)) {
		ipAddress = request.getHeader("HTTP_CLIENT_IP");
	}
	if (StringX.isBlank(ipAddress) || UNKNOWN_MAGIC.equalsIgnoreCase(ipAddress)) {
		ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
	}
	if (StringX.isBlank(ipAddress) || UNKNOWN_MAGIC.equalsIgnoreCase(ipAddress)) {
		ipAddress = request.getRemoteAddr();
	}
	//多个ip取第一个
	if (!StringX.isBlank(ipAddress) && ipAddress.contains(",")) {
		ipAddress = ipAddress.trim().split(",")[0];
	}
	if (StringX.isBlank(ipAddress)) {
		ipAddress = UNKNOWN_MAGIC;
	}
	return ipAddress;
}
```

### 根据IP获取城市
使用taobao接口来解析IP所在城市【该接口不稳定】
- 返回json格式
```json
{"code":0,"data":{"ip":"116.226.208.59","country":"中国","area":"","region":"上海","city":"上海","county":"XX","isp":"电信","country_id":"CN","area_id":"","region_id":"310000","city_id":"310100","county_id":"xx","isp_id":"100017"}}
```

- 代码
```java
public static String getCityByIP(String ip){
	String aliIPUrl = "http://ip.taobao.com/service/getIpInfo.php?ip="+ip;
	String result ="unknown";
	try {
		SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
		RestTemplate restTemplate = new RestTemplate(requestFactory);
		String resultJson = restTemplate.getForObject("http://ip.taobao.com/service/getIpInfo.php?ip={ip}", String.class,ip);
		ObjectMapper mapper = new ObjectMapper();
		Map ipMap=mapper.readValue(resultJson, Map.class);
		if("0".equals(ipMap.get("code")+"")){
			result =   StringX.nvl((String)((Map)ipMap.get("data")).get("city"),"unknown");
		}
	} catch (Exception e) {
	   log.warn("[解析IP发生异常:ip="+ip+"] "+e.getMessage());
	}
	log.info("[解析IP:"+ip+"] -> "+result);
	return  result;
}
```

- 执行示例
![](https://img.yebukong.com/blog/1105352334948560898/getIP.png)

:smiley:

------------
> 本文由 [叶不空](https://yebukong.com "叶不空") 创作,采用 [知识共享署名 4.0 国际许可协议](https://creativecommons.org/licenses/by/4.0/ "知识共享署名 4.0 国际许可协议")进行许可,转载请附上链接!
> 本文链接: [https://yebukong.com/article/1105352334948560898.html](https://yebukong.com/article/1105352334948560898.html "Java后端在Nginx代理下获取请求的真实IP")
                        
(°ο°)评论插件未能完成加载!