问题现象
- 我们的平台使用Spring Cloud微服务架构,使用Spring Boot构建Java服务,使用google的jib插件打成docker镜像包
- 我们使用docker虚拟化部署,使用docker-compose统一管理所有服务,包括Java服务和nginx等组件
- 我们前后端分离,前端通过nginx访问我们的网关(Spring Cloud Gateway),再转发到对应的Java服务
- 我们需要记录一些基础业务数据变动日志,于是在过滤器里拦截对应请求记录日志
- 在记录操作的来源ip时,记录了一个
172.18.0.x
的地址,这个明显不是实际客户端来源的ip
排查解决
- 我们使用
getRemoteAddres(request)
获取的ip地址,按理说是能获取到客户端的真实ip地址的 - 地址不对,想着从request的header里直接获取,参考网上的方法,查看哪个header参数里有ip地址
log.info("X-Real-IP={}", request.getHeader("X-Real-IP"));
log.info("X-Original-Forwarded-For={}", request.getHeader("X-Original-Forwarded-For"));
log.info("X-Forwarded-For={}", request.getHeader("X-Forwarded-For"));
log.info("x-forwarded-for={}", request.getHeader("x-forwarded-for"));
log.info("Proxy-Client-IP={}", request.getHeader("Proxy-Client-IP"));
log.info("WL-Proxy-Client-IP={}", request.getHeader("WL-Proxy-Client-IP"));
log.info("HTTP_CLIENT_IP={}", request.getHeader("HTTP_CLIENT_IP"));
log.info("HTTP_X_FORWARDED_FOR={}", request.getHeader("HTTP_X_FORWARDED_FOR"));
-
结果发现,只有
X-Forwarded-For
能获取到地址,还是那个错误的172.18.0.x
的地址
-
地址不对,应该是哪里出了问题,可能是docker网络、nginx代理或者gateway网关
-
进一步排查,这个地址之前看到过,
172.1x.0.x
,是docker网络生成的ip地址 -
使用docker命令
docker network ls
,查看了docker网络后,发现我们确实用的是这个
-
继续查看各个docker服务的ip,确定下这个ip是哪个服务的,具体来说,是nginx的、gateway的,还是具体的这个Java应用的
-
使用
docker exec -it 服务名 /bin/bash
进入docker容器内部,使用cat /etc/hosts
查看网络配置 -
对比发现这个ip是nginx服务的,说明获取客户端远程地址时,获取到了nginx的ip
-
nginx是决定能获取到正确的客户端请求ip地址的,因为它的log日志输出里,是有来源ip的
修改nginx配置
- 查看了nginx的配置文件
default.conf
,发现里面没有其他配置,已有的X-Forwarded-For
配置为proxy_set_header X-Forwarded-For $proxy_protocol_addr;
,这里直接把nginx代理服务自己的地址赋给了X-Forwarded-For
,所以我们获取到的是nginx的地址 - 我们现在需要做的,主要是在主配置文件,添加一行
proxy_set_header X-Real-IP $remote_addr;
,将客户端的真实ip地址,赋给X-Real-IP
- 执行命令
docker restart nginx
,重启nginx使其生效
Java代码
- Java代码修改也很简单,对应nginx的配置,获取
X-Real-IP
即可
public static String getIpAddress(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
- 成功获取,结束
nginx配置proxy_set_header介绍
在Nginx配置中,proxy_set_header
指令是用于定义向代理服务器传递的请求头字段。该指令专门用于location
块中,并且通常配合 proxy_pass
指令一起工作,proxy_pass
指令定义了代理服务器的协议和地址。
基本上,当Nginx作为反向代理服务器时,客户端的请求首先到达Nginx,然后Nginx将这些请求转发到后端的上游服务器。在转发请求时,Nginx可以设置或修改请求头。proxy_set_header
指令正是用来进行这样的设置或修改。
下面是几个proxy_set_header
常见用例:
-
传递主机名 - 将客户端请求的原主机头信息传递到上游服务器。
proxy_set_header Host $host;
-
传递真实IP地址 - 将客户端的真实IP地址传递给后端应用,这在后端应用需要记录真实的客户端地址时非常有用。
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-
传递HTTPS相关信息 - 当Nginx用作SSL终结时,它可以告诉后端应用请求是通过HTTPS或HTTP进行的。
proxy_set_header X-Forwarded-Proto $scheme;
-
用户的Worker处理状态: 有时,应用程序可能需要知道客户端连接的具体状态。
proxy_set_header Connection $connection_upgrade;
标准的 proxy_set_header
指令使用方法如下:
proxy_set_header Header-Name Header-Value;
Header-Name
是你希望设置的HTTP请求头名称。Header-Value
是对应的值,它可能是一个固定的字符串,也可以是Nginx提供的变量,如$remote_addr
、$http_user_agent
、$http_cookie
等。
注意,默认情况下,Nginx会使用某些标准请求头,如Host
、Connection
等,如果你没有明确使用proxy_set_header
设置它们,Nginx会传递它的默认值。
在调整Nginx作为反向代理服务器时,正确配置proxy_set_header
指令能确保后端服务器可以接收到所需的所有重要信息,提供正确和安全的服务。