feat(iot):【网关设备:70%】动态注册的初步实现(未测试),基于 stateful-sauteeing-pillow.md 规划

This commit is contained in:
YunaiV
2026-01-25 11:16:07 +08:00
parent 1309be39c3
commit 38a21ad59c
22 changed files with 663 additions and 94 deletions

View File

@@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO;
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;
import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;
/**
* IoT 设备通用 API
@@ -28,4 +30,12 @@ public interface IotDeviceCommonApi {
*/
CommonResult<IotDeviceRespDTO> getDevice(IotDeviceGetReqDTO infoReqDTO);
/**
* 设备动态注册(一型一密)
*
* @param reqDTO 动态注册请求
* @return 注册结果(包含 DeviceSecret
*/
CommonResult<IotDeviceRegisterRespDTO> registerDevice(IotDeviceRegisterReqDTO reqDTO);
}

View File

@@ -33,9 +33,10 @@ public enum IotDeviceMessageMethodEnum implements ArrayValuable<String> {
TOPO_CHANGE("thing.topo.change", "拓扑关系变更通知", false),
// ========== 设备注册 ==========
// 可参考https://help.aliyun.com/zh/iot/user-guide/register-devices
// 可参考https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification
SUB_DEVICE_REGISTER("thing.sub.register", "设备动态注册", true),
DEVICE_REGISTER("thing.auth.register", "设备动态注册", true),
SUB_DEVICE_REGISTER("thing.auth.register.sub", "子设备动态注册", true),
// ========== 设备属性 ==========
// 可参考https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services

View File

@@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.iot.core.topic.auth;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
/**
* IoT 设备动态注册 Request DTO
* <p>
* 用于直连设备/网关的一型一密动态注册:使用 ProductSecret 验证签名,返回 DeviceSecret
*
* @author 芋道源码
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a>
*/
@Data
public class IotDeviceRegisterReqDTO {
/**
* 产品标识
*/
@NotEmpty(message = "产品标识不能为空")
private String productKey;
/**
* 设备名称
*/
@NotEmpty(message = "设备名称不能为空")
private String deviceName;
// TODO @AI可以去掉 random 字段;
/**
* 随机数,用于签名
*/
@NotEmpty(message = "随机数不能为空")
private String random;
// TODO @AI看起来是直接带 productSecret 阿里云上,你在检查下!
/**
* 签名
*/
@NotEmpty(message = "签名不能为空")
private String sign;
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.iot.core.topic.auth;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* IoT 设备动态注册 Response DTO
* <p>
* 用于直连设备/网关的一型一密动态注册响应
*
* @author 芋道源码
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a>
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class IotDeviceRegisterRespDTO {
/**
* 产品标识
*/
private String productKey;
/**
* 设备名称
*/
private String deviceName;
/**
* 设备密钥
*/
private String deviceSecret;
}

View File

@@ -6,10 +6,12 @@ import lombok.Data;
/**
* IoT 子设备动态注册 Request DTO
* <p>
* 用于 thing.sub.register 消息的 params 数组元素
* 用于 thing.auth.register.sub 消息的 params 数组元素
*
* 特殊:网关子设备的动态注册,必须已经创建好该网关子设备(不然哪来的 {@link #deviceName} 字段)。更多的好处,是设备不用提前烧录 deviceSecret 密钥。
*
* @author 芋道源码
* @see <a href="http://help.aliyun.com/zh/marketplace/dynamic-registration-of-sub-devices">阿里云 - 动态注册子设备</a>
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/register-devices">阿里云 - 动态注册子设备</a>
*/
@Data
public class IotSubDeviceRegisterReqDTO {

View File

@@ -7,10 +7,10 @@ import lombok.NoArgsConstructor;
/**
* IoT 子设备动态注册 Response DTO
* <p>
* 用于 thing.sub.register 响应的设备信息
* 用于 thing.auth.register.sub 响应的设备信息
*
* @author 芋道源码
* @see <a href="http://help.aliyun.com/zh/marketplace/dynamic-registration-of-sub-devices">阿里云 - 动态注册子设备</a>
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/register-devices">阿里云 - 动态注册子设备</a>
*/
@Data
@NoArgsConstructor

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.iot.core.util;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.digest.HmacAlgorithm;
@@ -87,4 +88,62 @@ public class IotDeviceAuthUtils {
return new DeviceInfo().setProductKey(usernameParts[1]).setDeviceName(usernameParts[0]);
}
// ========== 动态注册相关方法 ==========
// TODO @AI想了下还是放回到对应的 productService、deviceService 更合适;
/**
* 生成产品密钥
*
* @return 产品密钥UUID
*/
public static String generateProductSecret() {
return IdUtil.fastSimpleUUID();
}
/**
* 生成设备密钥
*
* @return 设备密钥UUID
*/
public static String generateDeviceSecret() {
return IdUtil.fastSimpleUUID();
}
// TODO @AI去掉 random
/**
* 计算动态注册签名
* <p>
* 参考阿里云规范,参数按字典序排列拼接
*
* @param productSecret 产品密钥
* @param productKey 产品标识
* @param deviceName 设备名称
* @param random 随机数
* @return 签名
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">一型一密</a>
*/
public static String buildRegisterSign(String productSecret, String productKey, String deviceName, String random) {
String content = "deviceName" + deviceName + "productKey" + productKey + "random" + random;
return DigestUtil.hmac(HmacAlgorithm.HmacSHA256, StrUtil.utf8Bytes(productSecret))
.digestHex(content);
}
// TODO @AI是不是调用方自己验证就好了不要这里面抽
/**
* 验证动态注册签名
*
* @param productSecret 产品密钥
* @param productKey 产品标识
* @param deviceName 设备名称
* @param random 随机数
* @param sign 待验证的签名
* @return 是否验证通过
*/
public static boolean verifyRegisterSign(String productSecret, String productKey,
String deviceName, String random, String sign) {
String expectedSign = buildRegisterSign(productSecret, productKey, deviceName, random);
return expectedSign.equals(sign);
}
}