【功能完善】IoT: 更新 EMQX 插件配置,添加 MQTT 连接参数,重构相关逻辑

This commit is contained in:
安浩浩
2025-02-22 22:50:37 +08:00
parent 8cf8af1f6d
commit 53697b55c2
11 changed files with 161 additions and 92 deletions

View File

@@ -62,7 +62,7 @@ public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi {
@Override
public CommonResult<Boolean> authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO) {
Boolean result = deviceUpstreamService.authenticateEmqxConnection(authReqDTO);
boolean result = deviceUpstreamService.authenticateEmqxConnection(authReqDTO);
return success(result);
}

View File

@@ -79,18 +79,6 @@ public class IotDeviceRespVO {
@ExcelProperty("设备密钥")
private String deviceSecret;
@Schema(description = "MQTT 客户端 ID", example = "24602")
@ExcelProperty("MQTT 客户端 ID")
private String mqttClientId;
@Schema(description = "MQTT 用户名", example = "芋艿")
@ExcelProperty("MQTT 用户名")
private String mqttUsername;
@Schema(description = "MQTT 密码")
@ExcelProperty("MQTT 密码")
private String mqttPassword;
@Schema(description = "认证类型(如一机一密、动态注册)", example = "2")
@ExcelProperty("认证类型(如一机一密、动态注册)")
private String authType;

View File

@@ -67,6 +67,6 @@ public interface IotDeviceUpstreamService {
*
* @param authReqDTO Emqx 连接认证 DTO
*/
Boolean authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO);
boolean authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO);
}

View File

@@ -174,7 +174,7 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
}
private void registerDevice0(String productKey, String deviceName, Long gatewayId,
IotDeviceUpstreamAbstractReqDTO registerReqDTO) {
IotDeviceUpstreamAbstractReqDTO registerReqDTO) {
// 1.1 注册设备
IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(productKey, deviceName);
boolean registerNew = device == null;
@@ -280,16 +280,15 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
sendDeviceMessage(message, device);
}
// TODO @haohao建议返回 boolean
@Override
public Boolean authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO) {
public boolean authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO) {
log.info("[authenticateEmqxConnection][认证 Emqx 连接: {}]", authReqDTO);
// 1. 校验设备是否存在
// username 格式:${DeviceName}&${ProductKey}
String[] usernameParts = authReqDTO.getUsername().split("&");
if (usernameParts.length != 2) {
log.error("[authenticateEmqxConnection][认证失败username 格式不正确]");
return Boolean.FALSE;
return false;
}
String deviceName = usernameParts[0];
String productKey = usernameParts[1];
@@ -298,19 +297,18 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService {
if (device == null) {
log.error("[authenticateEmqxConnection][设备({}/{}) 不存在]",
productKey, deviceName);
return Boolean.FALSE;
return false;
}
// 2. 校验密码
String deviceSecret = device.getDeviceSecret();
String clientId = authReqDTO.getClientId();
MqttSignResult sign = MqttSignUtils.calculate(productKey, deviceName, deviceSecret, clientId);
// TODO @haohaonotEquals尽量不走取反逻辑哈
if (!StrUtil.equals(sign.getPassword(), authReqDTO.getPassword())) {
log.error("[authenticateEmqxConnection][认证失败,密码不正确]");
return Boolean.FALSE;
if (StrUtil.equals(sign.getPassword(), authReqDTO.getPassword())) {
log.info("[authenticateEmqxConnection][认证成功]");
return true;
}
log.info("[authenticateEmqxConnection][认证成功]");
return Boolean.TRUE;
log.error("[authenticateEmqxConnection][认证失败,密码不正确]");
return false;
}
private void updateDeviceLastTime(IotDeviceDO device, IotDeviceUpstreamAbstractReqDTO reqDTO) {

View File

@@ -1,9 +1,10 @@
package cn.iocoder.yudao.module.iot.util;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import lombok.AllArgsConstructor;
import lombok.Getter;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
/**
@@ -13,10 +14,6 @@ import java.nio.charset.StandardCharsets;
*/
public class MqttSignUtils {
private static final String SIGN_METHOD = "hmacsha256";
// TODO @haohaocalculate 方法,可以融合么?
/**
* 计算 MQTT 连接参数
*
@@ -26,14 +23,7 @@ public class MqttSignUtils {
* @return 包含 clientId, username, password 的结果对象
*/
public static MqttSignResult calculate(String productKey, String deviceName, String deviceSecret) {
String clientId = productKey + "." + deviceName;
String username = deviceName + "&" + productKey;
// 生成 password
// TODO @haohaosignContent 和 signContentBuilder 风格保持统一的实现哈
String signContent = String.format("clientId%sdeviceName%sdeviceSecret%sproductKey%s",
clientId, deviceName, deviceSecret, productKey);
String password = sign(signContent, deviceSecret);
return new MqttSignResult(clientId, username, password);
return calculate(productKey, deviceName, deviceSecret, productKey + "." + deviceName);
}
/**
@@ -47,56 +37,31 @@ public class MqttSignUtils {
*/
public static MqttSignResult calculate(String productKey, String deviceName, String deviceSecret, String clientId) {
String username = deviceName + "&" + productKey;
String signContentBuilder = "clientId" + clientId +
"deviceName" + deviceName +
"deviceSecret" + deviceSecret +
"productKey" + productKey;
// 构建签名内容
StringBuilder signContentBuilder = new StringBuilder()
.append("clientId").append(clientId)
.append("deviceName").append(deviceName)
.append("deviceSecret").append(deviceSecret)
.append("productKey").append(productKey);
String password = sign(signContentBuilder, deviceSecret);
// 使用 HMac 计算签名
byte[] key = deviceSecret.getBytes(StandardCharsets.UTF_8);
String signContent = signContentBuilder.toString();
HMac mac = new HMac(HmacAlgorithm.HmacSHA256, key);
String password = mac.digestHex(signContent);
return new MqttSignResult(clientId, username, password);
}
// TODO @haohaohutool 貌似有工具类可以用哈。
private static String sign(String content, String key) {
try {
Mac mac = Mac.getInstance(SIGN_METHOD);
mac.init(new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), SIGN_METHOD));
byte[] signData = mac.doFinal(content.getBytes(StandardCharsets.UTF_8));
return bytesToHex(signData);
} catch (Exception e) {
throw new RuntimeException("Failed to sign content with HmacSHA256", e);
}
}
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
String hex = Integer.toHexString(0xFF & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
/**
* MQTT 签名结果类
*/
@Getter
// TODO @haohao可以用 lombok 哈
@AllArgsConstructor
public static class MqttSignResult {
private final String clientId;
private final String username;
private final String password;
public MqttSignResult(String clientId, String username, String password) {
this.clientId = clientId;
this.username = username;
this.password = password;
}
}
}