这是一个非常简单的问题,但是在信息传播过程中,发生了一些错误。
比如一些人会说,你配置 X-Real-Ip 啊,这样就行了。
或者有些人会问,X-Real-Ip 和 X-Forward-For 有什么区别,原理是什么?
俨然是把 X-Real-Ip 给误解了。
本文带你看看,到底什么是 X-Real-Ip。
快速回答
X-Real-Ip 什么也不是。你可以使用 My-Real-Ip,His-Real-Ip,随便什么字符串。
1server {
2 ...
3 location / {
4 ...
5 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
6 proxy_set_header Host $host;
7 proxy_set_header His-Real-Ip2 $remote_addr;
8 }
9}
以上配置设置了三个请求头,分别是 X-Forwarded-For、Host、His-Real-Ip2,后台 java 服务端可以根据 His-Real-Ip2 获取真实的 IP。
听起来很伤人,可是 X-Real-Ip 真的只是看起来是官方的、某个隐藏的字段,实际上它并不是。
不要再问什么 X-Real-Ip 的原理是什么?
或者 特殊的请求头 X-Real-Ip
这样的问题了,实际上真正的值,是 nginx 的内置变量 $remote_addr。
Docker 中的 nginx
docker 中的 nginx 可以获取到的 $remote_addr,可能是 172.*.0.1。这些地址,是容器网桥的地址。
那么如何获取到用户的真实 ip 呢?你的 nginx 容器必须使用主机网络。
以下是一个 docker compose 示例:
1 my-nginx:
2 restart: always
3 container_name: my-nginx
4 image: nginx:latest
5 network_mode: host # note this mode is host
6 my-server:
7 restart: always
8 container_name: my-server
9 networks:
10 - spring_cloud_default
11 networks:
12 spring_cloud_default:
13 driver: bridge
在你的后端代码中,考虑生产和开发环境,你应该做如下判断(以 java 为例,假设我们使用 His-Real-Ip2):
1RequestAttributes ra = RequestContextHolder.getRequestAttributes();
2ServletRequestAttributes sra = (ServletRequestAttributes) ra;
3if (null != sra) {
4 HttpServletRequest request = sra.getRequest();
5 realIp = request.getHeader("His-Real-Ip"); // 假设你有两个 nginx,做了两层转发,第一层 nginx 设置了 His-Real-Ip
6 if (notValidIp(realIp)) { // notValidIp 可以自己实现,比如不以 172 开头,或者 192.168 等等开头
7 realIp = request.getHeader("His-Real-Ip2"); // 第二层 nginx 设置了 His-Real-Ip2。通过这样的设置,就能区分哪些流量是走网关进来,哪些是直连进来。没什么用,只是作为一个说明
8 if (notValidIp(realIp)) {
9 realIp = request.getRemoteAddr();
10 }
11 }
12}
结束语
好了,在技术工作过程中,我们不要以讹传讹,虽然这是一个简单的问题,但是要说清它,并不简单。