Java获取被nginx代理的emqx客户端真实ip

Java获取被nginx代理的emqx客户端真实ip

契机

⚙ 使用nginx作为负载均衡(Load Balancing)的时候,发现真实ip无法获取。几经折腾终于拿到真实ip,又发现被代理的端口又无法使用非代理模式连接,由于之前暴露的docker端口有限,从中部分取巧终于拨开云雾见光明,再结合Java客户端获取真实ip一气呵成。

EMQX配置

#docker部署
#18083为管理页面端口,1883为默认mqtt端口,8883为默认mqtts端口
#如果还没有新建emqx容器,建议多暴露端口,具体原因见下文
docker run -d --name emqx -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 8883:8883 -p 18083:18083 emqx/emqx:5.7.2
  • 进入emqx管理页面,新建proxy_tcp监听器,类型为tcp,端口为8883
  • 然后关闭之前ssl-8883监听
  • 借用端口是因为docker暴露到宿主机的端口不够了,一旦设置比如proxy_tcp监听器的8883代理监听后,客户端就只能通过nginx代理连接这个8883端口了,直接连接8883就不行了

请添加图片描述

#获取emqx的容器id,假如为1111
docker ps | grep emqx #将配置文件拷贝出来
docker cp 1111:/opt/emqx/etc/emqx.conf .#修改,添加下面项目
vim emqx.conf #打开proxy_tcp的代理监听
listeners.tcp.proxy_tcp {proxy_protocol = true
}#关闭default的代理,默认是关闭的
#但是一旦打开过,就要手动设置为false
listeners.tcp.default {proxy_protocol = false
}#放置回去
docker cp ./emqx.conf 1111:/opt/emqx/etc/#重启容器
docker restart 1111

此时

  • 直接使用客户端设备连接8883端口失败,无论mqtt/mqtts协议
  • 1883端口可以使用mqtt协议可以正常连接
  • emqx不存放证书,ssl认证是一个耗时的操作,丢到nginx去搞

nginx配置

#nginx安装 - 略#nginx安装模块
sudo yum install nginx-mod-stream#修改nginx配置文件
vim /etc/nginx/nginx.conf#配置文件如下
#192.168.0.1为emqx所在服务器,与nginx不在一个服务器
#监听1883为mqtt
#监听8883为mqtts
stream {upstream stream_backend {zone tcp_servers 64k;hash $remote_addr;server 192.168.0.1:1883 max_fails=2 fail_timeout=30s;}server {listen 1883;proxy_pass stream_backend;proxy_buffer_size 4k;}upstream stream_backend_ssl {zone tcp_servers 64k;hash $remote_addr;server 192.168.0.1:8883 max_fails=2 fail_timeout=30s;}server {listen 8883 ssl;proxy_protocol on;        proxy_pass stream_backend_ssl;proxy_buffer_size 4k;ssl_handshake_timeout 15s;ssl_certificate     /etc/nginx/cert/_.xx.pem;ssl_certificate_key /etc/nginx/cert/_.xx.key;}}#重启nginx
sudo systemctl restart nginx

此时

  • 8883为mqtts端口,链接mqtts,emqx控制台可以看到真实ip
  • 1883为mqtt端口,链接mqtt,emqx控制台看不到真实ip
  • 理论上业务只暴露mqtts端口到外网,mqtt为内部调试方便抓包用的

请添加图片描述

脚本获取

设置api密钥,保存下username和pasword

请添加图片描述

#!/bin/bash# Check if both host and password arguments are provided
if [ $# -lt 2 ]; thenecho "Usage: $0 <host> <password>"exit 1
fi# Define host, username, and password
HOST="$1"
USERNAME="username"
PASSWORD="$2"# Encode username and password in Base64
AUTH=$(echo -n "$USERNAME:$PASSWORD" | base64)# Create the curl command
curl -H "Authorization: Basic $AUTH" "http://$HOST:18083/api/v5/clients?like_username=server"
#运行
chmod +x ./emqx_curl.sh
./emqx_curl.sh localhost your_password_here

Java获取

service

package com.bothsavage.common.mqtt.service;import com.alibaba.fastjson.JSONObject;
import com.bothsavage.common.mqtt.config.interceptor.EmqxAuthInterceptor;
import com.bothsavage.common.mqtt.entity.dto.ClientInfoDTO;
import com.bothsavage.common.mqtt.entity.dto.EmqxRespDTO;import feign.Headers;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;/*** Emqx请求** @author bothSavage*/
@FeignClient(name = "emqxRmiService", url = "${emqx.rmi.url:http://127.0.0.1:18083/}",configuration = EmqxAuthInterceptor.class)
public interface EmqxRmiService {/*** 获取客户端信息** @param clientId 客户端id* @return -*/@GetMapping(value = "/api/v5/clients/{id}")@Headers(value = {"Content-Type=application/json;charset=UTF-8", "Accept=application/json"})ClientInfoDTO getClientById(@PathVariable("id") String clientId);/*** 查询客户端信息** @param likeUserName 客户端名称* @return -*/@GetMapping(value = "/api/v5/clients")@Headers(value = {"Content-Type=application/json;charset=UTF-8", "Accept=application/json"})EmqxRespDTO<ClientInfoDTO> getClientsByUserName(@RequestParam("like_username") String likeUserName);}

auth

package com.bothsavage.common.mqtt.config.interceptor;import feign.RequestInterceptor;
import feign.RequestTemplate;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;import java.util.Base64;/*** Emqx请求权限拦截** @author bothSavage*/
@Configuration
public class EmqxAuthInterceptor implements RequestInterceptor {@Value("${emqx.rmi.username:x}")private String username;@Value("${emqx.rmi.password:x}")private String password;@Overridepublic void apply(RequestTemplate template) {String auth = username + ":" + password;String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());template.header("Authorization", "Basic " + encodedAuth);}}

dto

package com.bothsavage.common.mqtt.entity.dto;import com.alibaba.fastjson.annotation.JSONField;import lombok.Data;/*** dto** @author bothSavage*/
@Data
public class ClientInfoDTO {@JSONField(name = "clientid")private String clientid;@JSONField(name = "mqueue_len")private Integer mqueueLen;@JSONField(name = "reductions")private Integer reductions;@JSONField(name = "keepalive")private Integer keepalive;@JSONField(name = "listener")private String listener;@JSONField(name = "proto_ver")private Integer protoVer;@JSONField(name = "recv_msg.dropped.await_pubrel_timeout")private Integer recvMsgDroppedAwaitPubrelTimeout;@JSONField(name = "send_msg.dropped.expired")private Integer sendMsgDroppedExpired;@JSONField(name = "mountpoint")private Object mountpoint;@JSONField(name = "mailbox_len")private Integer mailboxLen;@JSONField(name = "send_msg")private Integer sendMsg;@JSONField(name = "zone")private String zone;@JSONField(name = "subscriptions_cnt")private Integer subscriptionsCnt;@JSONField(name = "heap_size")private Integer heapSize;@JSONField(name = "recv_msg")private Integer recvMsg;@JSONField(name = "recv_cnt")private Integer recvCnt;@JSONField(name = "send_msg.dropped.too_large")private Integer sendMsgDroppedTooLarge;@JSONField(name = "awaiting_rel_cnt")private Integer awaitingRelCnt;@JSONField(name = "subscriptions_max")private String subscriptionsMax;@JSONField(name = "recv_msg.qos0")private Integer recvMsgQos0;@JSONField(name = "recv_msg.qos1")private Integer recvMsgQos1;@JSONField(name = "recv_msg.qos2")private Integer recvMsgQos2;@JSONField(name = "node")private String node;@JSONField(name = "inflight_max")private Integer inflightMax;@JSONField(name = "port")private Integer port;@JSONField(name = "recv_pkt")private Integer recvPkt;@JSONField(name = "send_oct")private Integer sendOct;@JSONField(name = "inflight_cnt")private Integer inflightCnt;@JSONField(name = "awaiting_rel_max")private String awaitingRelMax;@JSONField(name = "is_persistent")private Boolean isPersistent;@JSONField(name = "send_msg.dropped")private Integer sendMsgDropped;@JSONField(name = "recv_msg.dropped")private Integer recvMsgDropped;@JSONField(name = "clean_start")private Boolean cleanStart;@JSONField(name = "send_msg.qos0")private Integer sendMsgQos0;@JSONField(name = "created_at")private String createdAt;@JSONField(name = "connected_at")private String connectedAt;@JSONField(name = "enable_authn")private Boolean enableAuthn;@JSONField(name = "mqueue_dropped")private Integer mqueueDropped;@JSONField(name = "is_bridge")private Boolean isBridge;@JSONField(name = "send_msg.dropped.queue_full")private Integer sendMsgDroppedQueueFull;@JSONField(name = "proto_name")private String protoName;@JSONField(name = "ip_address")private String ipAddress;@JSONField(name = "send_cnt")private Integer sendCnt;@JSONField(name = "connected")private Boolean connected;@JSONField(name = "recv_oct")private Integer recvOct;@JSONField(name = "mqueue_max")private Integer mqueueMax;@JSONField(name = "send_msg.qos2")private Integer sendMsgQos2;@JSONField(name = "send_msg.qos1")private Integer sendMsgQos1;@JSONField(name = "expiry_interval")private Integer expiryInterval;@JSONField(name = "send_pkt")private Integer sendPkt;@JSONField(name = "username")private String username;}

test

clientIp = emqxRmiService.getClientById("clientId").getIpAddress();

总结

  • 路线清晰,只是操作起来稍微麻烦点
  • 注意:emqx打开了代理的访问的监听器,只能通过nginx来中转,无法直接链接了
  • 当前只用了一个emqx,没有做集群
  • docker端口暴露需要前期规划好,要不然特别麻烦

写到最后

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/94139.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/94139.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Jenkins自动化部署服务到Kubernetes环境

在现代软件开发中,持续集成和持续部署(CI/CD)已成为提高开发效率和软件质量的关键实践。本文将介绍如何使用Jenkins自动化部署服务到Kubernetes环境,并重点介绍Maven与私服的配置。 环境准备 在开始之前,请确保您已准备好以下环境: Jenkins服务器 Kubernetes集群 Docker镜…

OpenAI重新开源!gpt-oss-20b适配昇腾并上线魔乐社区

2025年8月5日&#xff0c;OpenAI发布了两款全新的开源权重语言模型&#xff0c;均为混合专家&#xff08;MoE&#xff09;架构&#xff0c;其规模设计可在消费级GPU和云端的多种硬件上高效运行。这些模型采用 Apache 2.0 许可协议&#xff0c;因此可用于蒸馏到其他推理模型中、…

SpringCloud入门(简洁明了)

目录 一.创建微服务项目 (一)环境准备 (二)项目结构图 (三)流程 二. Nacos (一)注册中心 1.服务注册 2.服务发现 3.编写微服务API 4.远程调用基本实现 5.负载均衡 6.LoadBalanced注解式注解均衡 7.注册中心宕机&#xff0c;远程调用还能成功吗 (二)配置中心 1.基…

集成算法学习笔记

一、集成算法简介1. 核心思想类比“多个专家综合判断优于单个专家”&#xff0c;通过构建并结合多个个体学习器&#xff0c;提升模型的泛化能力&#xff08;降低过拟合风险、提高预测准确性&#xff09;&#xff0c;完成复杂的学习任务。2. 个体学习器与结合模块个体学习器&…

让Chrome信任自签名证书

让Chrome信任自签名证书&#xff08;Unix系列OS&#xff09; 背景 想在本地测试自己写的基于HTTPS连接的Web应用&#xff0c;跑在3001端口。但使用Chrome浏览器访问https://localhost:3001时显示连接不安全。解决了但没解决 使用mkcert一键创建证书&#xff1a; mkcert localho…

[江科大库]基于 OpenMV 的矩形识别与 STM32 串口通信(电子设计大赛实用教程)

🎯 基于 OpenMV 的矩形识别与 STM32 串口通信(电子设计大赛实用教程) 一、前言 在本科生电子设计大赛中,经常会遇到图像识别相关的任务,例如: 识别 矩形框(如识别一个 A4 纸、黑色标记框等); 将识别结果传输到 STM32 单片机,用于后续控制(舵机、移动小车、机械臂…

人脸识别驱动的工厂人体属性检测与预警机制

人体属性检测&#xff1a;人脸识别智慧检测驱动的工厂管理革新&#xff08;所有图片均为真实项目案例&#xff09;在制造业数字化转型浪潮中&#xff0c;人体属性检测技术已成为破解传统工厂管理难题的核心工具。通过融合人脸识别智慧检测、目标检测算法与多模态数据分析&#…

数据工程师——ETL

ETL面试题01 一、基础概念与理论类 1. 请解释什么是 ETL?它在数据处理流程中扮演什么角色? 答:ETL就是数据抽取、转化、加载。目的是将分散的数据源集中在一起进行处理分析。 数据抽取:是指各种数据源中抽取数据,包括关系型数据库(MySQL、Oracle等)、日志文件、Exce…

Oracle APEX 经典报表中的Checkbox

目录 1. 建表&#xff06;投入测试数据 2. 经典报表做成 2-1. 画面布局如下​编辑 2-2. 报表使用的SQL 2-3. RS列的Heading设定 2-4. Function and Global Variable Declaration 2-5. Execute when Page Loads 2-6. Process 3. 运行效果​编辑 1. 建表&#xff06;投入…

Codeforces Round 1043 (Div.3)

比赛连接&#xff1a;Codeforces Round 1043 (Div.3) A. Homework 题目链接&#xff1a;A - Homework Vlad and Dima have been assigned a task in school for their English class. They were given two strings aaa and bbb and asked to append all characters from bbb …

GPS欺骗式干扰的产生

我们在GNSS抗干扰天线的选型、测试方法以及为什么不能做RTK&#xff1f;&#xff08;抗干扰内容全集&#xff09;中提到的抗干扰天线&#xff0c;针对的是GPS压制式干扰。对于GPS欺骗式干扰&#xff0c;抗干扰天线是无能为力的。 简单来说&#xff0c;压制式干扰是通过发射强功…

[PV]AXI R/W/RW带宽计算的tcl脚本

AXI R/W/RW带宽计算的tcl脚本 我基于前述的axi_read_bw_per_id.tcl脚本进行了修改,使其支持: 读通道(Read Channel):计算基于rvalid && rready的有效周期(已在前述实现)。 写通道(Write Channel):计算基于wvalid && wready的有效周期,考虑wstrb的ac…

阿里云AnalyticDB同步数据至华为云taurusdb

1 概述 AnalyticDB和taurusdb都是高度兼容mysql协议的数据库&#xff0c;从现有的AnalyticDB官方数据同步方案来看&#xff0c;只有FlinkSQL合适。 同步方案官方文档&#xff1a; https://help.aliyun.com/zh/analyticdb/analyticdb-for-mysql/user-guide/flink-subscribes-b…

学习嵌入式之驱动——系统移植(二)

一、uboot常用命令与环境变量1.命令&#xff1a;&#xff08;1&#xff09;环境变量操作命令命令功能格式printenv 查看环境变量printenvsetenv新建/修改环境变量setenv 环境变量名 环境变量值saveenv保存环境变量saveenv&#xff08;2&#xff09;内存操作命令命令功能格式示例…

EasyExcel 合并单元格最佳实践:基于注解的自动合并与样式控制

EasyExcel 合并单元格最佳实践&#xff1a;基于注解的自动合并与样式控制 前言 在日常开发中&#xff0c;我们经常需要导出 Excel 报表&#xff0c;而合并单元格是提升报表可读性的常见需求。本文将介绍如何基于 EasyExcel 实现智能的单元格合并功能&#xff0c;通过自定义注解…

Unity设置UI显示区域

系列文章目录 untiy工具 文章目录 系列文章目录 👉前言 👉一、效果图 👉二、制作过程(检测中心点位置) 👉2-1、代码实现 👉三、优化为检测整个UI四个角点 👉四、性能优化建议 👉壁纸分享 👉总结 👉前言 思路: 获取屏幕的宽度和高度,定义中间区域的范围…

Qt中用于图像缩放的核⼼⽅法QPixmap::scaled

QPixmap::scaled是Qt中用于图像缩放的核⼼⽅法&#xff0c;其作⽤和⽤法如下&#xff1a;‌一、核心作用‌‌图像尺寸调整‌根据指定尺寸对图像进⾏等⽐例或⾮等⽐例缩放&#xff0c;⽀持放⼤和缩⼩操作。‌保持宽高比‌通过AspectRatioMode参数控制是否保持原始图像的宽⾼⽐。…

SQL Workbench/J:一款免费开源、跨平台的通用SQL查询工具

SQL Workbench/J 是一款基于 Java 开发的免费开源、跨平台的通用 SQL 查询工具。 SQL Workbench/J 主要专注于 SQL 脚本开发和数据导入导出功能&#xff0c;不提供各种数据库管理功能。 功能特性 跨平台&#xff1a;可以在任何安装了 Java 运行时环境的操作系统上运行&#xf…

DOLO 上涨:Berachain 生态爆发的前奏?

在 Berachain 生态逐渐进入公众视野之际&#xff0c;Dolomite&#xff08;简称 Dolomite&#xff0c;代币 DOLO&#xff09;成为链上表现最为突出的明星协议。其代币价格在短短两个月内&#xff0c;从 $0.03 飙升至 $0.3&#xff0c;涨幅接近 10 倍。市场不仅将其视作 Berachai…

吉利汽车与芯鼎微成立联合创新实验室共谱车规级LCoS显示新篇章

2025年8月20日&#xff0c;吉利汽车研究院技术规划中心副主任李莉、光学实验室负责人李金桦博士等一行四人莅临芯鼎微&#xff0c;双方共同为"吉利汽车-芯鼎微联合创新实验室"揭牌&#xff0c;标志着两家企业在车载先进显示技术领域迈入深度协同创新的新阶段。 在这汽…