【功能完善】IoT: 更新 EMQX 插件配置,添加 MQTT 连接参数,重构相关逻辑
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -67,6 +67,6 @@ public interface IotDeviceUpstreamService {
|
||||
*
|
||||
* @param authReqDTO Emqx 连接认证 DTO
|
||||
*/
|
||||
Boolean authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO);
|
||||
boolean authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO);
|
||||
|
||||
}
|
||||
|
@@ -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 @haohao:notEquals,尽量不走取反逻辑哈
|
||||
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) {
|
||||
|
@@ -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 @haohao:calculate 方法,可以融合么?
|
||||
|
||||
/**
|
||||
* 计算 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 @haohao:signContent 和 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 @haohao:hutool 貌似有工具类可以用哈。
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user