当前位置: 首页 > news >正文

docker:Java通过nginx获取客户端的真实ip地址

问题现象

  • 我们的平台使用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会使用某些标准请求头,如HostConnection等,如果你没有明确使用proxy_set_header设置它们,Nginx会传递它的默认值。

在调整Nginx作为反向代理服务器时,正确配置proxy_set_header指令能确保后端服务器可以接收到所需的所有重要信息,提供正确和安全的服务。

相关文章:

  • 【云原生之kubernetes实战】在k8s环境下部署Mikochi文件管理工具
  • 【STM32调试】寄存器调试不良问题记录持续版
  • etcd安装
  • Idea 开发环境不断切换git代码分支导致冲掉别人代码
  • 运用分布式锁 redisson
  • 第十五章 : Spring Cloud全链路监控(Pinpoint实战)
  • Docker(二)安装指南:主要介绍在 Linux 、Windows 10 和 macOS 上的安装
  • 准备的一些爬虫面试题
  • 《WebKit 技术内幕》学习之七(2): 渲染基础
  • IPv6自动隧道---6to4中继
  • 【隧道篇 / SSL】(7.4) ❀ 01. 只允许国内IP通过SSL VPN访问内网 ❀ FortiGate 防火墙
  • 开发者广告变现对接广告平台,广告瀑布流是什么?
  • GAMES104-现代游戏引擎:从入门到实践 - 物理引擎课程笔记汇总
  • ISA Server 2006部署网站对比nginx
  • k8s和knative的区别与联系
  • 2017届校招提前批面试回顾
  • angular组件开发
  • git 常用命令
  • Javascript编码规范
  • LeetCode18.四数之和 JavaScript
  • Mysql数据库的条件查询语句
  • scrapy学习之路4(itemloder的使用)
  • SpiderData 2019年2月13日 DApp数据排行榜
  • 爱情 北京女病人
  • 复习Javascript专题(四):js中的深浅拷贝
  • 什么软件可以剪辑音乐?
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 提醒我喝水chrome插件开发指南
  • scrapy中间件源码分析及常用中间件大全
  • ​LeetCode解法汇总518. 零钱兑换 II
  • ​比特币大跌的 2 个原因
  • ​人工智能书单(数学基础篇)
  • ![CDATA[ ]] 是什么东东
  • (01)ORB-SLAM2源码无死角解析-(66) BA优化(g2o)→闭环线程:Optimizer::GlobalBundleAdjustemnt→全局优化
  • (02)Hive SQL编译成MapReduce任务的过程
  • (二)springcloud实战之config配置中心
  • (三)Honghu Cloud云架构一定时调度平台
  • (学习日记)2024.01.19
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (转)大型网站架构演变和知识体系
  • .describe() python_Python-Win32com-Excel
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .NET Core日志内容详解,详解不同日志级别的区别和有关日志记录的实用工具和第三方库详解与示例
  • .net 流——流的类型体系简单介绍
  • .NET 命令行参数包含应用程序路径吗?
  • .NET连接数据库方式
  • @Autowired多个相同类型bean装配问题
  • [ Linux 长征路第五篇 ] make/Makefile Linux项目自动化创建工具
  • [ vulhub漏洞复现篇 ] Grafana任意文件读取漏洞CVE-2021-43798
  • [AIGC codze] Kafka 的 rebalance 机制
  • [Angular 基础] - 表单:响应式表单
  • [C#]winform制作仪表盘好用的表盘控件和使用方法
  • [C++]——带你学习类和对象
  • [CTF]2022美团CTF WEB WP
  • [EFI]DELL XPS13 9360电脑 Hackintosh 黑苹果efi引导文件