物联网实战|Spring Boot MQTT平台 | emqx配置开启tls
基于emqx v6.2.2 版本
开启 SSL/TLS 连接
SSL/TLS 加密功能会在传输层对网络连接进行加密,它能在提升通信数据安全性的同时,保证数据的完整性
安全优势
- 强认证:开启 TLS 连接后,通讯双方将互相检查对方的身份,比如通过检查对方持有的 X.509 数字证书;这类数字证书通常是由受信机构 CA(Certificate Authority)颁发,不可伪造。
- 机密性:开启 TLS 连接后,每次会话都会根据双方协商得到的会话密钥进行加密。任何第三方都无法知晓通讯内容,因此即使一次会话的密钥泄露,也不影响其他会话的安全性。
- 完整性:加密通讯中的数据被篡改的可能性极低
单/双向认证对比
EMQX 提供了非常完整的 SSL/TLS 能力支持,支持通过 X.509 证书实现单向和双向客户端/服务器互信认证
| 认证方式 | 说明 | 验证方式 | 优缺点 |
|---|---|---|---|
| 单向认证 | 客户端验证服务器身份,但服务器不验证客户端的身份 | 客户端通常不需要提供证书,仅需验证服务器的证书是否由受信任的证书颁发机构(CA)签发 | 只能实现通信数据的机密性和完整性,但无法保证通信双方的身份 |
| 双向认证 | 服务器和客户端彼此验证对方的身份 | 需要为每个设备签发证书,服务器验证客户端的证书以确认其身份的合法性 | 可以确保服务器和客户端之间的互信关系,并防止中间人攻击 |
开启和配置认证
证书生成方式
获取证书的方式(自签名证书、申请或购买由受信任 CA 签发的证书)
自签名证书
前置准备
已安装 OpenSSL
- 第一步:执行如下命令
openssl genrsa -des3 -out emqx.key 2048
输入密码 123456
openssl req -x509 -new -nodes -key emqx.key -sha256 -days 3650 -out emqx.crt
- 第二步:交互式选项填写
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some‑State]:Shanghai
Locality Name (eg, city) []:Shanghai
Organization Name (eg, company) [Internet Widgits Pty Ltd]:EMQX
Organizational Unit Name (eg, section) []:Server
Common Name (e.g. server FQDN or YOUR name) []:jysemel.top
Email Address []:jysemel.top

签发证书(客户端和服务器)一样
签发客户端证书的步骤与签发服务器证书类似,只是在生成 CSR 时,需要将 Common Name 设置为客户端的唯一标识,如用户名、客户端 ID 等
- 第一步:执行如下命令
openssl genrsa -out server.key 2048
- 第二步
openssl req -new -key server.key -out server.csr
- 第三步:交互式选项填写

- 第四步
openssl x509 -req -in server.csr -CA emqx.crt -CAkey emqx.key -CAcreateserial -out server.crt -days 365

配置监听器
进入管理 -> 监听器(如果是docker部署,监听器中开放端口必须docker同步开放)
docker构建emqx broker应用
docker run -d \
--name emqx \
--restart always \
-p 1883:1883 \
-p 8083:8083 \
-p 8084:8084 \
-p 8883:8883 \
-p 28084:18083 \
-p 18085:18085 \
-v /data/emqx/data:/opt/emqx/data \
-v /data/emqx/log:/opt/emqx/log \
-e EMQX_COPY_CONF=true \
emqx/emqx:latest
分别将server对应的证书上传托管

添加监听器

源码
https://gitee.com/kcnf-iot/iot-sample/tree/master/emqx
部分代码
本案例是本地采用ip,如上证书同样使用ip
package com.jysemel.iot.client;
import com.hivemq.client.mqtt.MqttClientSslConfig;
import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient;
import com.hivemq.client.mqtt.mqtt5.Mqtt5Client;
import com.hivemq.client.mqtt.mqtt5.message.auth.Mqtt5SimpleAuth;
import com.hivemq.client.mqtt.mqtt5.message.connect.Mqtt5Connect;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.net.ssl.TrustManagerFactory;
import java.io.InputStream;
import java.security.KeyStore;
import java.util.UUID;
@Slf4j
@Configuration
public class MqttConfig {
@Bean
public Mqtt5AsyncClient mqtt5AsyncClient() {
// 1. 加载CA根证书,构建SSL信任配置
MqttClientSslConfig sslConfig = buildSslConfig();
// 2. 构建MQTT TLS客户端
String clientId = "server_" + UUID.randomUUID().toString().substring(0, 16);
Mqtt5AsyncClient client = Mqtt5Client.builder()
.identifier(clientId)
.serverHost("192.168.0.19")
.serverPort(18085)
.sslConfig(sslConfig)
.buildAsync();
// 3. 构建连接报文
Mqtt5SimpleAuth simpleAuth = Mqtt5SimpleAuth.builder()
.username("emqx")
.password("123456".getBytes())
.build();
Mqtt5Connect connectMsg = Mqtt5Connect.builder()
.cleanStart(true)
.simpleAuth(simpleAuth)
.build();
// 4. 发起异步连接
client.connect(connectMsg)
.whenComplete((connAck, throwable) -> {
if (throwable != null) {
throwable.printStackTrace();
System.out.println("MQTT TLS连接失败 ");
return;
}
System.out.println("MQTT TLS连接成功,clientId ");
});
return client;
}
private MqttClientSslConfig buildSslConfig() {
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
try (InputStream is = MqttConfig.class.getResourceAsStream("/cert/emqx.jks")) {
if (is == null) {
throw new RuntimeException("信任库文件未找到: /cert/emqx.jks");
}
trustStore.load(is, "123456".toCharArray());
}
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
return MqttClientSslConfig.builder()
.trustManagerFactory(tmf)
// 关键:自定义 HostnameVerifier,总是验证通过(仅测试用!)
.hostnameVerifier((hostname, session) -> true)
.build();
} catch (Exception e) {
throw new RuntimeException("SSL 初始化失败", e);
}
}
}
结果


