Commit b4ce72ea7d4caec62b29ed4dcbf6f366a6266009

Authored by YunaiV
1 parent 38a21ad5

feat(iot):【网关设备:72%】动态注册的初步实现(未测试、额外优化代码),基于 stateful-sauteeing-pillow.md 规划

Showing 29 changed files with 313 additions and 457 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java
@@ -7,8 +7,10 @@ import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; @@ -7,8 +7,10 @@ import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
7 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 7 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
8 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO; 8 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO;
9 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; 9 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
  10 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO;
10 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; 11 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;
11 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; 12 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;
  13 +import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO;
12 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; 14 import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
13 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; 15 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
14 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; 16 import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
@@ -21,6 +23,8 @@ import org.springframework.web.bind.annotation.PostMapping; @@ -21,6 +23,8 @@ import org.springframework.web.bind.annotation.PostMapping;
21 import org.springframework.web.bind.annotation.RequestBody; 23 import org.springframework.web.bind.annotation.RequestBody;
22 import org.springframework.web.bind.annotation.RestController; 24 import org.springframework.web.bind.annotation.RestController;
23 25
  26 +import java.util.List;
  27 +
24 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 28 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
25 29
26 /** 30 /**
@@ -66,4 +70,11 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { @@ -66,4 +70,11 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi {
66 return success(deviceService.registerDevice(reqDTO)); 70 return success(deviceService.registerDevice(reqDTO));
67 } 71 }
68 72
  73 + @Override
  74 + @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/register-sub")
  75 + @PermitAll
  76 + public CommonResult<List<IotSubDeviceRegisterRespDTO>> registerSubDevices(@RequestBody IotSubDeviceRegisterFullReqDTO reqDTO) {
  77 + return success(deviceService.registerSubDevices(reqDTO));
  78 + }
  79 +
69 } 80 }
70 \ No newline at end of file 81 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java
@@ -49,7 +49,7 @@ public class IotProductSaveReqVO { @@ -49,7 +49,7 @@ public class IotProductSaveReqVO {
49 private String codecType; 49 private String codecType;
50 50
51 @Schema(description = "是否开启动态注册", example = "false") 51 @Schema(description = "是否开启动态注册", example = "false")
52 - @NotEmpty(message = "是否开启动态注册不能为空") 52 + @NotNull(message = "是否开启动态注册不能为空")
53 private Boolean registerEnabled; 53 private Boolean registerEnabled;
54 54
55 } 55 }
56 \ No newline at end of file 56 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
@@ -44,8 +44,8 @@ public interface ErrorCodeConstants { @@ -44,8 +44,8 @@ public interface ErrorCodeConstants {
44 ErrorCode DEVICE_SUB_REGISTER_PARAMS_INVALID = new ErrorCode(1_050_003_200, "子设备注册参数无效"); 44 ErrorCode DEVICE_SUB_REGISTER_PARAMS_INVALID = new ErrorCode(1_050_003_200, "子设备注册参数无效");
45 ErrorCode DEVICE_SUB_REGISTER_PRODUCT_NOT_GATEWAY_SUB = new ErrorCode(1_050_003_201, "产品【{}】不是网关子设备类型"); 45 ErrorCode DEVICE_SUB_REGISTER_PRODUCT_NOT_GATEWAY_SUB = new ErrorCode(1_050_003_201, "产品【{}】不是网关子设备类型");
46 ErrorCode DEVICE_REGISTER_DISABLED = new ErrorCode(1_050_003_210, "该产品未开启动态注册功能"); 46 ErrorCode DEVICE_REGISTER_DISABLED = new ErrorCode(1_050_003_210, "该产品未开启动态注册功能");
47 - ErrorCode DEVICE_REGISTER_SIGN_INVALID = new ErrorCode(1_050_003_211, "动态注册签名验证失败");  
48 - ErrorCode DEVICE_ALREADY_ACTIVATED = new ErrorCode(1_050_003_212, "设备已激活,不允许重复注册"); 47 + ErrorCode DEVICE_REGISTER_SECRET_INVALID = new ErrorCode(1_050_003_211, "产品密钥验证失败");
  48 + ErrorCode DEVICE_REGISTER_ALREADY_EXISTS = new ErrorCode(1_050_003_212, "设备已存在,不允许重复注册");
49 49
50 // ========== 产品分类 1-050-004-000 ========== 50 // ========== 产品分类 1-050-004-000 ==========
51 ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在"); 51 ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在");
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.service.device; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.service.device;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; 4 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
  6 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO;
6 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; 7 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
7 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 8 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
8 import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; 9 import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
@@ -44,18 +45,6 @@ public interface IotDeviceService { @@ -44,18 +45,6 @@ public interface IotDeviceService {
44 */ 45 */
45 void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO); 46 void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO);
46 47
47 - // TODO @芋艿:先这么实现。未来看情况,要不要自己实现  
48 -  
49 - /**  
50 - * 更新设备的所属网关  
51 - *  
52 - * @param id 编号  
53 - * @param gatewayId 网关设备 ID  
54 - */  
55 - default void updateDeviceGateway(Long id, Long gatewayId) {  
56 - updateDevice(new IotDeviceSaveReqVO().setId(id).setGatewayId(gatewayId));  
57 - }  
58 -  
59 /** 48 /**
60 * 更新设备状态 49 * 更新设备状态
61 * 50 *
@@ -359,20 +348,31 @@ public interface IotDeviceService { @@ -359,20 +348,31 @@ public interface IotDeviceService {
359 // ========== 设备动态注册 ========== 348 // ========== 设备动态注册 ==========
360 349
361 /** 350 /**
362 - * 处理子设备动态注册消息(网关设备上报) 351 + * 直连/网关设备动态注册
363 * 352 *
364 - * @param message 消息  
365 - * @param gatewayDevice 网关设备 353 + * @param reqDTO 动态注册请求
  354 + * @return 注册结果(包含 DeviceSecret)
  355 + */
  356 + IotDeviceRegisterRespDTO registerDevice(@Valid IotDeviceRegisterReqDTO reqDTO);
  357 +
  358 + /**
  359 + * 网关子设备动态注册
  360 + * <p>
  361 + * 与 {@link #handleSubDeviceRegisterMessage} 方法的区别:
  362 + * 该方法网关设备信息通过 reqDTO 参数传入,而 {@link #handleSubDeviceRegisterMessage} 方法通过 gatewayDevice 参数传入
  363 + *
  364 + * @param reqDTO 子设备注册请求(包含网关设备信息)
366 * @return 注册结果列表 365 * @return 注册结果列表
367 */ 366 */
368 - List<IotSubDeviceRegisterRespDTO> handleSubDeviceRegisterMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice); 367 + List<IotSubDeviceRegisterRespDTO> registerSubDevices(@Valid IotSubDeviceRegisterFullReqDTO reqDTO);
369 368
370 /** 369 /**
371 - * 设备动态注册(直连设备/网关) 370 + * 处理子设备动态注册消息(网关设备上报)
372 * 371 *
373 - * @param reqDTO 动态注册请求  
374 - * @return 注册结果(包含 DeviceSecret) 372 + * @param message 消息
  373 + * @param gatewayDevice 网关设备
  374 + * @return 注册结果列表
375 */ 375 */
376 - IotDeviceRegisterRespDTO registerDevice(@Valid IotDeviceRegisterReqDTO reqDTO); 376 + List<IotSubDeviceRegisterRespDTO> handleSubDeviceRegisterMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice);
377 377
378 } 378 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.service.device; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.service.device;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.BooleanUtil; 4 import cn.hutool.core.util.BooleanUtil;
  5 +import cn.hutool.core.util.IdUtil;
5 import cn.hutool.core.util.ObjUtil; 6 import cn.hutool.core.util.ObjUtil;
6 import cn.hutool.core.util.StrUtil; 7 import cn.hutool.core.util.StrUtil;
7 import cn.hutool.extra.spring.SpringUtil; 8 import cn.hutool.extra.spring.SpringUtil;
@@ -14,6 +15,7 @@ import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; @@ -14,6 +15,7 @@ import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
14 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; 15 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
15 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; 16 import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
16 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 17 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
  18 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO;
17 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; 19 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
18 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; 20 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
19 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 21 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
@@ -142,11 +144,13 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -142,11 +144,13 @@ public class IotDeviceServiceImpl implements IotDeviceService {
142 144
143 private void initDevice(IotDeviceDO device, IotProductDO product) { 145 private void initDevice(IotDeviceDO device, IotProductDO product) {
144 device.setProductId(product.getId()).setProductKey(product.getProductKey()) 146 device.setProductId(product.getId()).setProductKey(product.getProductKey())
145 - .setDeviceType(product.getDeviceType());  
146 - // 生成密钥  
147 - device.setDeviceSecret(IotDeviceAuthUtils.generateDeviceSecret());  
148 - // 设置设备状态为未激活  
149 - device.setState(IotDeviceStateEnum.INACTIVE.getState()); 147 + .setDeviceType(product.getDeviceType())
  148 + .setDeviceSecret(generateDeviceSecret()) // 生成密钥
  149 + .setState(IotDeviceStateEnum.INACTIVE.getState()); // 默认未激活
  150 + }
  151 +
  152 + private String generateDeviceSecret() {
  153 + return IdUtil.fastSimpleUUID();
150 } 154 }
151 155
152 @Override 156 @Override
@@ -448,7 +452,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -448,7 +452,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
448 public IotDeviceAuthInfoRespVO getDeviceAuthInfo(Long id) { 452 public IotDeviceAuthInfoRespVO getDeviceAuthInfo(Long id) {
449 IotDeviceDO device = validateDeviceExists(id); 453 IotDeviceDO device = validateDeviceExists(id);
450 // 使用 IotDeviceAuthUtils 生成认证信息 454 // 使用 IotDeviceAuthUtils 生成认证信息
451 - IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo( 455 + IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(
452 device.getProductKey(), device.getDeviceName(), device.getDeviceSecret()); 456 device.getProductKey(), device.getDeviceName(), device.getDeviceSecret());
453 return BeanUtils.toBean(authInfo, IotDeviceAuthInfoRespVO.class); 457 return BeanUtils.toBean(authInfo, IotDeviceAuthInfoRespVO.class);
454 } 458 }
@@ -496,7 +500,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -496,7 +500,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
496 @Override 500 @Override
497 public boolean authDevice(IotDeviceAuthReqDTO authReqDTO) { 501 public boolean authDevice(IotDeviceAuthReqDTO authReqDTO) {
498 // 1. 校验设备是否存在 502 // 1. 校验设备是否存在
499 - IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(authReqDTO.getUsername()); 503 + IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(authReqDTO.getUsername());
500 if (deviceInfo == null) { 504 if (deviceInfo == null) {
501 log.error("[authDevice][认证失败,username({}) 格式不正确]", authReqDTO.getUsername()); 505 log.error("[authDevice][认证失败,username({}) 格式不正确]", authReqDTO.getUsername());
502 return false; 506 return false;
@@ -510,7 +514,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -510,7 +514,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
510 } 514 }
511 515
512 // 2. 校验密码 516 // 2. 校验密码
513 - IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo(productKey, deviceName, device.getDeviceSecret()); 517 + IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(productKey, deviceName, device.getDeviceSecret());
514 if (ObjUtil.notEqual(authInfo.getPassword(), authReqDTO.getPassword())) { 518 if (ObjUtil.notEqual(authInfo.getPassword(), authReqDTO.getPassword())) {
515 log.error("[authDevice][设备({}/{}) 密码不正确]", productKey, deviceName); 519 log.error("[authDevice][设备({}/{}) 密码不正确]", productKey, deviceName);
516 return false; 520 return false;
@@ -666,7 +670,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -666,7 +670,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
666 670
667 private IotDeviceDO addDeviceTopo(IotDeviceDO gatewayDevice, IotDeviceAuthReqDTO subDeviceAuth) { 671 private IotDeviceDO addDeviceTopo(IotDeviceDO gatewayDevice, IotDeviceAuthReqDTO subDeviceAuth) {
668 // 1.1 解析子设备信息 672 // 1.1 解析子设备信息
669 - IotDeviceAuthUtils.DeviceInfo subDeviceInfo = IotDeviceAuthUtils.parseUsername(subDeviceAuth.getUsername()); 673 + IotDeviceIdentity subDeviceInfo = IotDeviceAuthUtils.parseUsername(subDeviceAuth.getUsername());
670 if (subDeviceInfo == null) { 674 if (subDeviceInfo == null) {
671 throw exception(DEVICE_TOPO_SUB_DEVICE_USERNAME_INVALID); 675 throw exception(DEVICE_TOPO_SUB_DEVICE_USERNAME_INVALID);
672 } 676 }
@@ -814,63 +818,78 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -814,63 +818,78 @@ public class IotDeviceServiceImpl implements IotDeviceService {
814 if (BooleanUtil.isFalse(product.getRegisterEnabled())) { 818 if (BooleanUtil.isFalse(product.getRegisterEnabled())) {
815 throw exception(DEVICE_REGISTER_DISABLED); 819 throw exception(DEVICE_REGISTER_DISABLED);
816 } 820 }
817 - // 1.3 验证签名  
818 - if (!IotDeviceAuthUtils.verifyRegisterSign(product.getProductSecret(),  
819 - reqDTO.getProductKey(), reqDTO.getDeviceName(), reqDTO.getRandom(), reqDTO.getSign())) {  
820 - throw exception(DEVICE_REGISTER_SIGN_INVALID); 821 + // 1.3 验证 productSecret
  822 + if (ObjUtil.notEqual(product.getProductSecret(), reqDTO.getProductSecret())) {
  823 + throw exception(DEVICE_REGISTER_SECRET_INVALID);
821 } 824 }
822 -  
823 - // 4. 查找设备(预注册模式:设备必须已存在)  
824 - // TODO @AI:设备不用提前有,这个有问题! 825 + // 1.4 校验设备是否已存在(已存在则不允许重复注册)
825 IotDeviceDO device = getSelf().getDeviceFromCache(reqDTO.getProductKey(), reqDTO.getDeviceName()); 826 IotDeviceDO device = getSelf().getDeviceFromCache(reqDTO.getProductKey(), reqDTO.getDeviceName());
826 - if (device == null) {  
827 - throw exception(DEVICE_NOT_EXISTS); 827 + if (device != null) {
  828 + throw exception(DEVICE_REGISTER_ALREADY_EXISTS);
828 } 829 }
829 830
830 - // 5. 校验设备是否已激活(已激活的设备不允许重复注册)  
831 - if (!Objects.equals(device.getState(), IotDeviceStateEnum.INACTIVE.getState())) {  
832 - throw exception(DEVICE_ALREADY_ACTIVATED);  
833 - }  
834 -  
835 - // 6. 返回设备密钥 831 + // 2.1 自动创建设备
  832 + IotDeviceSaveReqVO createReqVO = new IotDeviceSaveReqVO()
  833 + .setDeviceName(reqDTO.getDeviceName())
  834 + .setProductId(product.getId());
  835 + device = createDevice0(createReqVO);
  836 + log.info("[registerDevice][产品({}) 自动创建设备({})]",
  837 + reqDTO.getProductKey(), reqDTO.getDeviceName());
  838 + // 2.2 返回设备密钥
836 return new IotDeviceRegisterRespDTO(device.getProductKey(), device.getDeviceName(), device.getDeviceSecret()); 839 return new IotDeviceRegisterRespDTO(device.getProductKey(), device.getDeviceName(), device.getDeviceSecret());
837 } 840 }
838 841
839 @Override 842 @Override
  843 + public List<IotSubDeviceRegisterRespDTO> registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO) {
  844 + // 1. 校验网关设备
  845 + IotDeviceDO gatewayDevice = getSelf().getDeviceFromCache(reqDTO.getGatewayProductKey(), reqDTO.getGatewayDeviceName());
  846 +
  847 + // 2. 遍历注册每个子设备
  848 + return registerSubDevices0(gatewayDevice, reqDTO.getSubDevices());
  849 + }
  850 +
  851 + @Override
840 public List<IotSubDeviceRegisterRespDTO> handleSubDeviceRegisterMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice) { 852 public List<IotSubDeviceRegisterRespDTO> handleSubDeviceRegisterMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice) {
841 - // 1.1 校验网关设备类型  
842 - if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) {  
843 - throw exception(DEVICE_NOT_GATEWAY);  
844 - }  
845 - // 1.2 解析参数 853 + // 1. 解析参数
846 if (!(message.getParams() instanceof List)) { 854 if (!(message.getParams() instanceof List)) {
847 throw exception(DEVICE_SUB_REGISTER_PARAMS_INVALID); 855 throw exception(DEVICE_SUB_REGISTER_PARAMS_INVALID);
848 } 856 }
849 - List<IotSubDeviceRegisterReqDTO> paramsList = JsonUtils.convertList(message.getParams(),  
850 - IotSubDeviceRegisterReqDTO.class);  
851 - if (CollUtil.isEmpty(paramsList)) { 857 + List<IotSubDeviceRegisterReqDTO> subDevices = JsonUtils.convertList(message.getParams(), IotSubDeviceRegisterReqDTO.class);
  858 +
  859 + // 2. 遍历注册每个子设备
  860 + return registerSubDevices0(gatewayDevice, subDevices);
  861 + }
  862 +
  863 + private List<IotSubDeviceRegisterRespDTO> registerSubDevices0(IotDeviceDO gatewayDevice,
  864 + List<IotSubDeviceRegisterReqDTO> subDevices) {
  865 + // 1.1 校验网关设备
  866 + if (gatewayDevice == null) {
  867 + throw exception(DEVICE_NOT_EXISTS);
  868 + }
  869 + if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) {
  870 + throw exception(DEVICE_NOT_GATEWAY);
  871 + }
  872 + // 1.2 注册设备不能为空
  873 + if (CollUtil.isEmpty(subDevices)) {
852 throw exception(DEVICE_SUB_REGISTER_PARAMS_INVALID); 874 throw exception(DEVICE_SUB_REGISTER_PARAMS_INVALID);
853 } 875 }
854 876
855 // 2. 遍历注册每个子设备 877 // 2. 遍历注册每个子设备
856 - List<IotSubDeviceRegisterRespDTO> results = new ArrayList<>(paramsList.size());  
857 - for (IotSubDeviceRegisterReqDTO params : paramsList) { 878 + List<IotSubDeviceRegisterRespDTO> results = new ArrayList<>(subDevices.size());
  879 + for (IotSubDeviceRegisterReqDTO subDevice : subDevices) {
858 try { 880 try {
859 - IotDeviceDO device = registerSubDevice(gatewayDevice, params); 881 + IotDeviceDO device = registerSubDevice0(gatewayDevice, subDevice);
860 results.add(new IotSubDeviceRegisterRespDTO( 882 results.add(new IotSubDeviceRegisterRespDTO(
861 - params.getProductKey(), params.getDeviceName(), device.getDeviceSecret())); 883 + subDevice.getProductKey(), subDevice.getDeviceName(), device.getDeviceSecret()));
862 } catch (Exception ex) { 884 } catch (Exception ex) {
863 - log.error("[handleSubDeviceRegisterMessage][子设备({}/{}) 注册失败]",  
864 - params.getProductKey(), params.getDeviceName(), ex); 885 + log.error("[registerSubDevices0][子设备({}/{}) 注册失败]",
  886 + subDevice.getProductKey(), subDevice.getDeviceName(), ex);
865 } 887 }
866 } 888 }
867 -  
868 - // 3. 返回响应数据(包含成功注册的子设备列表)  
869 return results; 889 return results;
870 } 890 }
871 891
872 - // TODO @AI:阿里云的,设备必须存在;  
873 - private IotDeviceDO registerSubDevice(IotDeviceDO gatewayDevice, IotSubDeviceRegisterReqDTO params) { 892 + private IotDeviceDO registerSubDevice0(IotDeviceDO gatewayDevice, IotSubDeviceRegisterReqDTO params) {
874 // 1.1 校验产品 893 // 1.1 校验产品
875 IotProductDO product = productService.getProductByProductKey(params.getProductKey()); 894 IotProductDO product = productService.getProductByProductKey(params.getProductKey());
876 if (product == null) { 895 if (product == null) {
@@ -880,28 +899,28 @@ public class IotDeviceServiceImpl implements IotDeviceService { @@ -880,28 +899,28 @@ public class IotDeviceServiceImpl implements IotDeviceService {
880 if (!IotProductDeviceTypeEnum.isGatewaySub(product.getDeviceType())) { 899 if (!IotProductDeviceTypeEnum.isGatewaySub(product.getDeviceType())) {
881 throw exception(DEVICE_SUB_REGISTER_PRODUCT_NOT_GATEWAY_SUB, params.getProductKey()); 900 throw exception(DEVICE_SUB_REGISTER_PRODUCT_NOT_GATEWAY_SUB, params.getProductKey());
882 } 901 }
883 - // 1.3 查找设备是否已存在 902 + // 1.3 校验设备是否已存在(子设备动态注册:设备必须已预注册)
884 IotDeviceDO existDevice = getSelf().getDeviceFromCache(params.getProductKey(), params.getDeviceName()); 903 IotDeviceDO existDevice = getSelf().getDeviceFromCache(params.getProductKey(), params.getDeviceName());
885 - if (existDevice != null) {  
886 - // 校验是否绑定到当前网关  
887 - if (ObjUtil.notEqual(existDevice.getGatewayId(), gatewayDevice.getId())) {  
888 - throw exception(DEVICE_GATEWAY_BINDTO_EXISTS,  
889 - existDevice.getProductKey(), existDevice.getDeviceName());  
890 - }  
891 - // 已存在则返回设备信息  
892 - return existDevice; 904 + if (existDevice == null) {
  905 + throw exception(DEVICE_NOT_EXISTS);
  906 + }
  907 + // 1.4 校验是否绑定到其他网关
  908 + if (existDevice.getGatewayId() != null && ObjUtil.notEqual(existDevice.getGatewayId(), gatewayDevice.getId())) {
  909 + throw exception(DEVICE_GATEWAY_BINDTO_EXISTS,
  910 + existDevice.getProductKey(), existDevice.getDeviceName());
893 } 911 }
894 912
895 - // 2. 创建新设备  
896 - IotDeviceSaveReqVO createReqVO = new IotDeviceSaveReqVO()  
897 - .setDeviceName(params.getDeviceName())  
898 - .setProductId(product.getId())  
899 - .setGatewayId(gatewayDevice.getId());  
900 - IotDeviceDO newDevice = createDevice0(createReqVO);  
901 - log.info("[registerSubDevice][网关({}/{}) 注册子设备({}/{})]",  
902 - gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(),  
903 - newDevice.getProductKey(), newDevice.getDeviceName());  
904 - return newDevice; 913 + // 2. 绑定到网关(如果尚未绑定)
  914 + if (existDevice.getGatewayId() == null) {
  915 + // 2.1 更新数据库
  916 + deviceMapper.updateById(new IotDeviceDO().setId(existDevice.getId()).setGatewayId(gatewayDevice.getId()));
  917 + // 2.2 清空对应缓存
  918 + deleteDeviceCache(existDevice);
  919 + log.info("[registerSubDevice][网关({}/{}) 绑定子设备({}/{})]",
  920 + gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(),
  921 + existDevice.getProductKey(), existDevice.getDeviceName());
  922 + }
  923 + return existDevice;
905 } 924 }
906 925
907 } 926 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.service.product; @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.service.product;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 6 +import cn.hutool.core.util.IdUtil;
7 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; 7 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
8 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; 8 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO;
9 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; 9 import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO;
@@ -55,11 +55,15 @@ public class IotProductServiceImpl implements IotProductService { @@ -55,11 +55,15 @@ public class IotProductServiceImpl implements IotProductService {
55 // 2. 插入 55 // 2. 插入
56 IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class) 56 IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class)
57 .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus()) 57 .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus())
58 - .setProductSecret(IotDeviceAuthUtils.generateProductSecret()); 58 + .setProductSecret(generateProductSecret());
59 productMapper.insert(product); 59 productMapper.insert(product);
60 return product.getId(); 60 return product.getId();
61 } 61 }
62 62
  63 + private String generateProductSecret() {
  64 + return IdUtil.fastSimpleUUID();
  65 + }
  66 +
63 @Override 67 @Override
64 @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#updateReqVO.id") 68 @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#updateReqVO.id")
65 public void updateProduct(IotProductSaveReqVO updateReqVO) { 69 public void updateProduct(IotProductSaveReqVO updateReqVO) {
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/IotDeviceCommonApi.java
@@ -4,8 +4,12 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -4,8 +4,12 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 4 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO; 5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO;
6 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; 6 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
  7 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO;
7 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; 8 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;
8 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; 9 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;
  10 +import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO;
  11 +
  12 +import java.util.List;
9 13
10 /** 14 /**
11 * IoT 设备通用 API 15 * IoT 设备通用 API
@@ -31,11 +35,19 @@ public interface IotDeviceCommonApi { @@ -31,11 +35,19 @@ public interface IotDeviceCommonApi {
31 CommonResult<IotDeviceRespDTO> getDevice(IotDeviceGetReqDTO infoReqDTO); 35 CommonResult<IotDeviceRespDTO> getDevice(IotDeviceGetReqDTO infoReqDTO);
32 36
33 /** 37 /**
34 - * 设备动态注册(一型一密) 38 + * 直连/网关设备动态注册(一型一密)
35 * 39 *
36 * @param reqDTO 动态注册请求 40 * @param reqDTO 动态注册请求
37 * @return 注册结果(包含 DeviceSecret) 41 * @return 注册结果(包含 DeviceSecret)
38 */ 42 */
39 CommonResult<IotDeviceRegisterRespDTO> registerDevice(IotDeviceRegisterReqDTO reqDTO); 43 CommonResult<IotDeviceRegisterRespDTO> registerDevice(IotDeviceRegisterReqDTO reqDTO);
40 44
  45 + /**
  46 + * 网关子设备动态注册(网关代理转发)
  47 + *
  48 + * @param reqDTO 子设备注册请求(包含网关标识和子设备列表)
  49 + * @return 注册结果列表
  50 + */
  51 + CommonResult<List<IotSubDeviceRegisterRespDTO>> registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO);
  52 +
41 } 53 }
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceAuthReqDTO.java
1 package cn.iocoder.yudao.module.iot.core.biz.dto; 1 package cn.iocoder.yudao.module.iot.core.biz.dto;
2 2
3 import jakarta.validation.constraints.NotEmpty; 3 import jakarta.validation.constraints.NotEmpty;
  4 +import lombok.AllArgsConstructor;
4 import lombok.Data; 5 import lombok.Data;
  6 +import lombok.NoArgsConstructor;
5 7
6 /** 8 /**
7 * IoT 设备认证 Request DTO 9 * IoT 设备认证 Request DTO
@@ -9,6 +11,8 @@ import lombok.Data; @@ -9,6 +11,8 @@ import lombok.Data;
9 * @author 芋道源码 11 * @author 芋道源码
10 */ 12 */
11 @Data 13 @Data
  14 +@NoArgsConstructor
  15 +@AllArgsConstructor
12 public class IotDeviceAuthReqDTO { 16 public class IotDeviceAuthReqDTO {
13 17
14 /** 18 /**
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotSubDeviceRegisterFullReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.core.biz.dto;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;
  4 +import jakarta.validation.constraints.NotEmpty;
  5 +import jakarta.validation.constraints.NotNull;
  6 +import lombok.Data;
  7 +
  8 +import java.util.List;
  9 +
  10 +/**
  11 + * IoT 子设备动态注册 Request DTO
  12 + * <p>
  13 + * 额外包含了网关设备的标识信息
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Data
  18 +public class IotSubDeviceRegisterFullReqDTO {
  19 +
  20 + /**
  21 + * 网关设备 ProductKey
  22 + */
  23 + @NotEmpty(message = "网关产品标识不能为空")
  24 + private String gatewayProductKey;
  25 +
  26 + /**
  27 + * 网关设备 DeviceName
  28 + */
  29 + @NotEmpty(message = "网关设备名称不能为空")
  30 + private String gatewayDeviceName;
  31 +
  32 + /**
  33 + * 子设备注册列表
  34 + */
  35 + @NotNull(message = "子设备注册列表不能为空")
  36 + private List<IotSubDeviceRegisterReqDTO> subDevices;
  37 +
  38 +}
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotDeviceRegisterReqDTO.java
@@ -6,7 +6,7 @@ import lombok.Data; @@ -6,7 +6,7 @@ import lombok.Data;
6 /** 6 /**
7 * IoT 设备动态注册 Request DTO 7 * IoT 设备动态注册 Request DTO
8 * <p> 8 * <p>
9 - * 用于直连设备/网关的一型一密动态注册:使用 ProductSecret 验证签名,返回 DeviceSecret 9 + * 用于直连设备/网关的一型一密动态注册:使用 productSecret 验证,返回 deviceSecret
10 * 10 *
11 * @author 芋道源码 11 * @author 芋道源码
12 * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a> 12 * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a>
@@ -26,18 +26,10 @@ public class IotDeviceRegisterReqDTO { @@ -26,18 +26,10 @@ public class IotDeviceRegisterReqDTO {
26 @NotEmpty(message = "设备名称不能为空") 26 @NotEmpty(message = "设备名称不能为空")
27 private String deviceName; 27 private String deviceName;
28 28
29 - // TODO @AI:可以去掉 random 字段;  
30 /** 29 /**
31 - * 随机数,用于签名 30 + * 产品密钥
32 */ 31 */
33 - @NotEmpty(message = "随机数不能为空")  
34 - private String random;  
35 -  
36 - // TODO @AI:看起来,是直接带 productSecret 阿里云上,你在检查下!  
37 - /**  
38 - * 签名  
39 - */  
40 - @NotEmpty(message = "签名不能为空")  
41 - private String sign; 32 + @NotEmpty(message = "产品密钥不能为空")
  33 + private String productSecret;
42 34
43 } 35 }
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/event/IotDeviceEventPostReqDTO.java
@@ -8,7 +8,7 @@ import lombok.Data; @@ -8,7 +8,7 @@ import lombok.Data;
8 * 用于 thing.event.post 消息的 params 参数 8 * 用于 thing.event.post 消息的 params 参数
9 * 9 *
10 * @author 芋道源码 10 * @author 芋道源码
11 - * @see <a href="https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services">阿里云 - 设备上报事件</a> 11 + * @see <a href="http://help.aliyun.com/zh/marketplace/device-reporting-events">阿里云 - 设备上报事件</a>
12 */ 12 */
13 @Data 13 @Data
14 public class IotDeviceEventPostReqDTO { 14 public class IotDeviceEventPostReqDTO {
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/property/IotDevicePropertyPostReqDTO.java
@@ -11,7 +11,7 @@ import java.util.Map; @@ -11,7 +11,7 @@ import java.util.Map;
11 * 本质是一个 Map,key 为属性标识符,value 为属性值 11 * 本质是一个 Map,key 为属性标识符,value 为属性值
12 * 12 *
13 * @author 芋道源码 13 * @author 芋道源码
14 - * @see <a href="https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services">阿里云 - 设备上报属性</a> 14 + * @see <a href="http://help.aliyun.com/zh/marketplace/device-reporting-attributes">阿里云 - 设备上报属性</a>
15 */ 15 */
16 public class IotDevicePropertyPostReqDTO extends HashMap<String, Object> { 16 public class IotDevicePropertyPostReqDTO extends HashMap<String, Object> {
17 17
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceAuthUtils.java
1 package cn.iocoder.yudao.module.iot.core.util; 1 package cn.iocoder.yudao.module.iot.core.util;
2 2
3 -import cn.hutool.core.util.IdUtil;  
4 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
5 import cn.hutool.crypto.digest.DigestUtil; 4 import cn.hutool.crypto.digest.DigestUtil;
6 import cn.hutool.crypto.digest.HmacAlgorithm; 5 import cn.hutool.crypto.digest.HmacAlgorithm;
7 -import lombok.AllArgsConstructor;  
8 -import lombok.Data;  
9 -import lombok.NoArgsConstructor; 6 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
  7 +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
10 8
11 /** 9 /**
12 * IoT 设备【认证】的工具类,参考阿里云 10 * IoT 设备【认证】的工具类,参考阿里云
@@ -15,49 +13,12 @@ import lombok.NoArgsConstructor; @@ -15,49 +13,12 @@ import lombok.NoArgsConstructor;
15 */ 13 */
16 public class IotDeviceAuthUtils { 14 public class IotDeviceAuthUtils {
17 15
18 - /**  
19 - * 认证信息  
20 - */  
21 - @Data  
22 - @NoArgsConstructor  
23 - @AllArgsConstructor  
24 - public static class AuthInfo {  
25 -  
26 - /**  
27 - * 客户端 ID  
28 - */  
29 - private String clientId;  
30 -  
31 - /**  
32 - * 用户名  
33 - */  
34 - private String username;  
35 -  
36 - /**  
37 - * 密码  
38 - */  
39 - private String password;  
40 -  
41 - }  
42 -  
43 - /**  
44 - * 设备信息  
45 - */  
46 - @Data  
47 - public static class DeviceInfo {  
48 -  
49 - private String productKey;  
50 -  
51 - private String deviceName;  
52 -  
53 - }  
54 -  
55 - public static AuthInfo getAuthInfo(String productKey, String deviceName, String deviceSecret) { 16 + public static IotDeviceAuthReqDTO getAuthInfo(String productKey, String deviceName, String deviceSecret) {
56 String clientId = buildClientId(productKey, deviceName); 17 String clientId = buildClientId(productKey, deviceName);
57 String username = buildUsername(productKey, deviceName); 18 String username = buildUsername(productKey, deviceName);
58 String password = buildPassword(deviceSecret, 19 String password = buildPassword(deviceSecret,
59 buildContent(clientId, productKey, deviceName, deviceSecret)); 20 buildContent(clientId, productKey, deviceName, deviceSecret));
60 - return new AuthInfo(clientId, username, password); 21 + return new IotDeviceAuthReqDTO(clientId, username, password);
61 } 22 }
62 23
63 public static String buildClientId(String productKey, String deviceName) { 24 public static String buildClientId(String productKey, String deviceName) {
@@ -80,70 +41,12 @@ public class IotDeviceAuthUtils { @@ -80,70 +41,12 @@ public class IotDeviceAuthUtils {
80 "productKey" + productKey; 41 "productKey" + productKey;
81 } 42 }
82 43
83 - public static DeviceInfo parseUsername(String username) { 44 + public static IotDeviceIdentity parseUsername(String username) {
84 String[] usernameParts = username.split("&"); 45 String[] usernameParts = username.split("&");
85 if (usernameParts.length != 2) { 46 if (usernameParts.length != 2) {
86 return null; 47 return null;
87 } 48 }
88 - return new DeviceInfo().setProductKey(usernameParts[1]).setDeviceName(usernameParts[0]);  
89 - }  
90 -  
91 - // ========== 动态注册相关方法 ==========  
92 -  
93 - // TODO @AI:想了下,还是放回到对应的 productService、deviceService 更合适;  
94 -  
95 - /**  
96 - * 生成产品密钥  
97 - *  
98 - * @return 产品密钥(UUID)  
99 - */  
100 - public static String generateProductSecret() {  
101 - return IdUtil.fastSimpleUUID();  
102 - }  
103 -  
104 - /**  
105 - * 生成设备密钥  
106 - *  
107 - * @return 设备密钥(UUID)  
108 - */  
109 - public static String generateDeviceSecret() {  
110 - return IdUtil.fastSimpleUUID();  
111 - }  
112 -  
113 - // TODO @AI:去掉 random;  
114 - /**  
115 - * 计算动态注册签名  
116 - * <p>  
117 - * 参考阿里云规范,参数按字典序排列拼接  
118 - *  
119 - * @param productSecret 产品密钥  
120 - * @param productKey 产品标识  
121 - * @param deviceName 设备名称  
122 - * @param random 随机数  
123 - * @return 签名  
124 - * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">一型一密</a>  
125 - */  
126 - public static String buildRegisterSign(String productSecret, String productKey, String deviceName, String random) {  
127 - String content = "deviceName" + deviceName + "productKey" + productKey + "random" + random;  
128 - return DigestUtil.hmac(HmacAlgorithm.HmacSHA256, StrUtil.utf8Bytes(productSecret))  
129 - .digestHex(content);  
130 - }  
131 -  
132 - // TODO @AI:是不是调用方自己验证就好了,不要这里面抽;  
133 - /**  
134 - * 验证动态注册签名  
135 - *  
136 - * @param productSecret 产品密钥  
137 - * @param productKey 产品标识  
138 - * @param deviceName 设备名称  
139 - * @param random 随机数  
140 - * @param sign 待验证的签名  
141 - * @return 是否验证通过  
142 - */  
143 - public static boolean verifyRegisterSign(String productSecret, String productKey,  
144 - String deviceName, String random, String sign) {  
145 - String expectedSign = buildRegisterSign(productSecret, productKey, deviceName, random);  
146 - return expectedSign.equals(sign); 49 + return new IotDeviceIdentity(usernameParts[1], usernameParts[0]);
147 } 50 }
148 51
149 } 52 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/router/IotEmqxAuthEventHandler.java
@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
7 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; 7 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
8 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 8 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
9 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 9 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  10 +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
10 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 11 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
11 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; 12 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
12 import io.vertx.core.json.JsonObject; 13 import io.vertx.core.json.JsonObject;
@@ -201,7 +202,7 @@ public class IotEmqxAuthEventHandler { @@ -201,7 +202,7 @@ public class IotEmqxAuthEventHandler {
201 */ 202 */
202 private void handleDeviceStateChange(String username, boolean online) { 203 private void handleDeviceStateChange(String username, boolean online) {
203 // 1. 解析设备信息 204 // 1. 解析设备信息
204 - IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(username); 205 + IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username);
205 if (deviceInfo == null) { 206 if (deviceInfo == null) {
206 log.debug("[handleDeviceStateChange][跳过非设备({})连接]", username); 207 log.debug("[handleDeviceStateChange][跳过非设备({})连接]", username);
207 return; 208 return;
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpUpstreamProtocol.java
@@ -51,7 +51,7 @@ public class IotHttpUpstreamProtocol extends AbstractVerticle { @@ -51,7 +51,7 @@ public class IotHttpUpstreamProtocol extends AbstractVerticle {
51 router.post(IotHttpAuthHandler.PATH).handler(authHandler); 51 router.post(IotHttpAuthHandler.PATH).handler(authHandler);
52 IotHttpRegisterHandler registerHandler = new IotHttpRegisterHandler(); 52 IotHttpRegisterHandler registerHandler = new IotHttpRegisterHandler();
53 router.post(IotHttpRegisterHandler.PATH).handler(registerHandler); 53 router.post(IotHttpRegisterHandler.PATH).handler(registerHandler);
54 - IotHttpRegisterSubHandler registerSubHandler = new IotHttpRegisterSubHandler(this); 54 + IotHttpRegisterSubHandler registerSubHandler = new IotHttpRegisterSubHandler();
55 router.post(IotHttpRegisterSubHandler.PATH).handler(registerSubHandler); 55 router.post(IotHttpRegisterSubHandler.PATH).handler(registerSubHandler);
56 IotHttpUpstreamHandler upstreamHandler = new IotHttpUpstreamHandler(this); 56 IotHttpUpstreamHandler upstreamHandler = new IotHttpUpstreamHandler(this);
57 router.post(IotHttpUpstreamHandler.PATH).handler(upstreamHandler); 57 router.post(IotHttpUpstreamHandler.PATH).handler(upstreamHandler);
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAbstractHandler.java
@@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException; @@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException;
8 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 8 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
9 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 9 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
10 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 10 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
11 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 11 +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
12 import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; 12 import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService;
13 import io.vertx.core.Handler; 13 import io.vertx.core.Handler;
14 import io.vertx.core.http.HttpHeaders; 14 import io.vertx.core.http.HttpHeaders;
@@ -74,7 +74,7 @@ public abstract class IotHttpAbstractHandler implements Handler&lt;RoutingContext&gt; @@ -74,7 +74,7 @@ public abstract class IotHttpAbstractHandler implements Handler&lt;RoutingContext&gt;
74 } 74 }
75 75
76 // 校验 token 76 // 校验 token
77 - IotDeviceAuthUtils.DeviceInfo deviceInfo = deviceTokenService.verifyToken(token); 77 + IotDeviceIdentity deviceInfo = deviceTokenService.verifyToken(token);
78 Assert.notNull(deviceInfo, "设备信息不能为空"); 78 Assert.notNull(deviceInfo, "设备信息不能为空");
79 // 校验设备信息是否匹配 79 // 校验设备信息是否匹配
80 if (ObjUtil.notEqual(productKey, deviceInfo.getProductKey()) 80 if (ObjUtil.notEqual(productKey, deviceInfo.getProductKey())
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAuthHandler.java
@@ -9,7 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -9,7 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
9 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; 9 import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
10 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 10 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
11 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 11 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
12 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 12 +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
13 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; 13 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;
14 import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; 14 import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService;
15 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; 15 import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
@@ -72,7 +72,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { @@ -72,7 +72,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler {
72 throw exception(DEVICE_AUTH_FAIL); 72 throw exception(DEVICE_AUTH_FAIL);
73 } 73 }
74 // 2.2 生成 Token 74 // 2.2 生成 Token
75 - IotDeviceAuthUtils.DeviceInfo deviceInfo = deviceTokenService.parseUsername(username); 75 + IotDeviceIdentity deviceInfo = deviceTokenService.parseUsername(username);
76 Assert.notNull(deviceInfo, "设备信息不能为空"); 76 Assert.notNull(deviceInfo, "设备信息不能为空");
77 String token = deviceTokenService.createToken(deviceInfo.getProductKey(), deviceInfo.getDeviceName()); 77 String token = deviceTokenService.createToken(deviceInfo.getProductKey(), deviceInfo.getDeviceName());
78 Assert.notBlank(token, "生成 token 不能为空位"); 78 Assert.notBlank(token, "生成 token 不能为空位");
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpRegisterHandler.java
@@ -33,7 +33,6 @@ public class IotHttpRegisterHandler extends IotHttpAbstractHandler { @@ -33,7 +33,6 @@ public class IotHttpRegisterHandler extends IotHttpAbstractHandler {
33 @Override 33 @Override
34 public CommonResult<Object> handle0(RoutingContext context) { 34 public CommonResult<Object> handle0(RoutingContext context) {
35 // 1. 解析参数 35 // 1. 解析参数
36 - // TODO @AI:参数不太对,看看我写的建议  
37 JsonObject body = context.body().asJsonObject(); 36 JsonObject body = context.body().asJsonObject();
38 String productKey = body.getString("productKey"); 37 String productKey = body.getString("productKey");
39 if (StrUtil.isEmpty(productKey)) { 38 if (StrUtil.isEmpty(productKey)) {
@@ -43,21 +42,14 @@ public class IotHttpRegisterHandler extends IotHttpAbstractHandler { @@ -43,21 +42,14 @@ public class IotHttpRegisterHandler extends IotHttpAbstractHandler {
43 if (StrUtil.isEmpty(deviceName)) { 42 if (StrUtil.isEmpty(deviceName)) {
44 throw invalidParamException("deviceName 不能为空"); 43 throw invalidParamException("deviceName 不能为空");
45 } 44 }
46 - String random = body.getString("random");  
47 - if (StrUtil.isEmpty(random)) {  
48 - throw invalidParamException("random 不能为空");  
49 - }  
50 - String sign = body.getString("sign");  
51 - if (StrUtil.isEmpty(sign)) {  
52 - throw invalidParamException("sign 不能为空"); 45 + String productSecret = body.getString("productSecret");
  46 + if (StrUtil.isEmpty(productSecret)) {
  47 + throw invalidParamException("productSecret 不能为空");
53 } 48 }
54 49
55 // 2. 调用动态注册 50 // 2. 调用动态注册
56 IotDeviceRegisterReqDTO reqDTO = new IotDeviceRegisterReqDTO() 51 IotDeviceRegisterReqDTO reqDTO = new IotDeviceRegisterReqDTO()
57 - .setProductKey(productKey)  
58 - .setDeviceName(deviceName)  
59 - .setRandom(random)  
60 - .setSign(sign); 52 + .setProductKey(productKey).setDeviceName(deviceName).setProductSecret(productSecret);
61 CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(reqDTO); 53 CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(reqDTO);
62 result.checkError(); 54 result.checkError();
63 55
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpRegisterSubHandler.java
@@ -2,12 +2,15 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; @@ -2,12 +2,15 @@ package cn.iocoder.yudao.module.iot.gateway.protocol.http.router;
2 2
3 import cn.hutool.extra.spring.SpringUtil; 3 import cn.hutool.extra.spring.SpringUtil;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;  
6 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
7 -import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;  
8 -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; 5 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  6 +import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
  7 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO;
  8 +import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO;
  9 +import io.vertx.core.json.JsonObject;
9 import io.vertx.ext.web.RoutingContext; 10 import io.vertx.ext.web.RoutingContext;
10 11
  12 +import java.util.List;
  13 +
11 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 14 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
12 15
13 /** 16 /**
@@ -27,13 +30,10 @@ public class IotHttpRegisterSubHandler extends IotHttpAbstractHandler { @@ -27,13 +30,10 @@ public class IotHttpRegisterSubHandler extends IotHttpAbstractHandler {
27 */ 30 */
28 public static final String PATH = "/auth/register/sub-device/:productKey/:deviceName"; 31 public static final String PATH = "/auth/register/sub-device/:productKey/:deviceName";
29 32
30 - private final IotHttpUpstreamProtocol protocol;  
31 -  
32 - private final IotDeviceMessageService deviceMessageService; 33 + private final IotDeviceCommonApi deviceApi;
33 34
34 - public IotHttpRegisterSubHandler(IotHttpUpstreamProtocol protocol) {  
35 - this.protocol = protocol;  
36 - this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class); 35 + public IotHttpRegisterSubHandler() {
  36 + this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);
37 } 37 }
38 38
39 @Override 39 @Override
@@ -42,18 +42,19 @@ public class IotHttpRegisterSubHandler extends IotHttpAbstractHandler { @@ -42,18 +42,19 @@ public class IotHttpRegisterSubHandler extends IotHttpAbstractHandler {
42 String productKey = context.pathParam("productKey"); 42 String productKey = context.pathParam("productKey");
43 String deviceName = context.pathParam("deviceName"); 43 String deviceName = context.pathParam("deviceName");
44 44
45 - // 2.1 解析消息  
46 - byte[] bytes = context.body().buffer().getBytes();  
47 - IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(bytes, productKey, deviceName);  
48 - // 2.2 设置方法  
49 - message.setMethod(IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod()); 45 + // 2. 解析子设备列表
  46 + JsonObject body = context.body().asJsonObject();
  47 + List<cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO> subDevices = JsonUtils.parseArray(
  48 + body.getJsonArray("params").toString(), cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO.class);
50 49
51 - // TODO @AI:可能还是需要一个新的 deviceApi 接口。因为 register sub 子设备不太一行;  
52 - // 2.3 发送消息  
53 - Object responseData = deviceMessageService.sendDeviceMessage(message, productKey, deviceName, protocol.getServerId()); 50 + // 3. 调用子设备动态注册
  51 + IotSubDeviceRegisterFullReqDTO reqDTO = new IotSubDeviceRegisterFullReqDTO()
  52 + .setGatewayProductKey(productKey).setGatewayDeviceName(deviceName).setSubDevices(subDevices);
  53 + CommonResult<List<IotSubDeviceRegisterRespDTO>> result = deviceApi.registerSubDevices(reqDTO);
  54 + result.checkError();
54 55
55 - // 3. 返回结果  
56 - return success(responseData); 56 + // 4. 返回结果
  57 + return success(result.getData());
57 } 58 }
58 59
59 } 60 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttUpstreamHandler.java
@@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; @@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
9 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO; 9 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO;
10 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; 10 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
11 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 11 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  12 +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
12 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 13 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
13 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; 14 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol;
14 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager; 15 import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager;
@@ -214,7 +215,7 @@ public class IotMqttUpstreamHandler { @@ -214,7 +215,7 @@ public class IotMqttUpstreamHandler {
214 } 215 }
215 216
216 // 4. 获取设备信息 217 // 4. 获取设备信息
217 - IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(username); 218 + IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username);
218 if (deviceInfo == null) { 219 if (deviceInfo == null) {
219 log.warn("[authenticateDevice][用户名格式不正确,客户端 ID: {},用户名: {}]", clientId, username); 220 log.warn("[authenticateDevice][用户名格式不正确,客户端 ID: {},用户名: {}]", clientId, username);
220 return false; 221 return false;
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqttws/router/IotMqttWsUpstreamHandler.java
@@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; @@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
10 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO; 10 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO;
11 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; 11 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
12 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 12 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  13 +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
13 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 14 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
14 import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.IotMqttWsUpstreamProtocol; 15 import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.IotMqttWsUpstreamProtocol;
15 import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.manager.IotMqttWsConnectionManager; 16 import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.manager.IotMqttWsConnectionManager;
@@ -521,7 +522,7 @@ public class IotMqttWsUpstreamHandler { @@ -521,7 +522,7 @@ public class IotMqttWsUpstreamHandler {
521 } 522 }
522 523
523 // 3. 获取设备信息 524 // 3. 获取设备信息
524 - IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(username); 525 + IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username);
525 if (deviceInfo == null) { 526 if (deviceInfo == null) {
526 log.warn("[authenticateDevice][用户名格式不正确,username: {}]", username); 527 log.warn("[authenticateDevice][用户名格式不正确,username: {}]", username);
527 return null; 528 return null;
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/router/IotTcpUpstreamHandler.java
@@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
11 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 11 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
12 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; 12 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
13 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 13 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  14 +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
14 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 15 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
15 import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpBinaryDeviceMessageCodec; 16 import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpBinaryDeviceMessageCodec;
16 import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpJsonDeviceMessageCodec; 17 import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpJsonDeviceMessageCodec;
@@ -162,7 +163,7 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; { @@ -162,7 +163,7 @@ public class IotTcpUpstreamHandler implements Handler&lt;NetSocket&gt; {
162 } 163 }
163 164
164 // 2.1 解析设备信息 165 // 2.1 解析设备信息
165 - IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.getUsername()); 166 + IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.getUsername());
166 if (deviceInfo == null) { 167 if (deviceInfo == null) {
167 sendErrorResponse(socket, message.getRequestId(), "解析设备信息失败", codecType); 168 sendErrorResponse(socket, message.getRequestId(), "解析设备信息失败", codecType);
168 return; 169 return;
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenService.java
1 package cn.iocoder.yudao.module.iot.gateway.service.auth; 1 package cn.iocoder.yudao.module.iot.gateway.service.auth;
2 2
3 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 3 +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
4 4
5 /** 5 /**
6 * IoT 设备 Token Service 接口 6 * IoT 设备 Token Service 接口
@@ -24,7 +24,7 @@ public interface IotDeviceTokenService { @@ -24,7 +24,7 @@ public interface IotDeviceTokenService {
24 * @param token 设备 Token 24 * @param token 设备 Token
25 * @return 设备信息 25 * @return 设备信息
26 */ 26 */
27 - IotDeviceAuthUtils.DeviceInfo verifyToken(String token); 27 + IotDeviceIdentity verifyToken(String token);
28 28
29 /** 29 /**
30 * 解析用户名 30 * 解析用户名
@@ -32,6 +32,6 @@ public interface IotDeviceTokenService { @@ -32,6 +32,6 @@ public interface IotDeviceTokenService {
32 * @param username 用户名 32 * @param username 用户名
33 * @return 设备信息 33 * @return 设备信息
34 */ 34 */
35 - IotDeviceAuthUtils.DeviceInfo parseUsername(String username); 35 + IotDeviceIdentity parseUsername(String username);
36 36
37 } 37 }
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenServiceImpl.java
@@ -5,6 +5,7 @@ import cn.hutool.json.JSONObject; @@ -5,6 +5,7 @@ import cn.hutool.json.JSONObject;
5 import cn.hutool.jwt.JWT; 5 import cn.hutool.jwt.JWT;
6 import cn.hutool.jwt.JWTUtil; 6 import cn.hutool.jwt.JWTUtil;
7 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; 7 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
  8 +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity;
8 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 9 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
9 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; 10 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
10 import jakarta.annotation.Resource; 11 import jakarta.annotation.Resource;
@@ -48,7 +49,7 @@ public class IotDeviceTokenServiceImpl implements IotDeviceTokenService { @@ -48,7 +49,7 @@ public class IotDeviceTokenServiceImpl implements IotDeviceTokenService {
48 } 49 }
49 50
50 @Override 51 @Override
51 - public IotDeviceAuthUtils.DeviceInfo verifyToken(String token) { 52 + public IotDeviceIdentity verifyToken(String token) {
52 Assert.notBlank(token, "token 不能为空"); 53 Assert.notBlank(token, "token 不能为空");
53 // 校验 JWT Token 54 // 校验 JWT Token
54 boolean verify = JWTUtil.verify(token, gatewayProperties.getToken().getSecret().getBytes()); 55 boolean verify = JWTUtil.verify(token, gatewayProperties.getToken().getSecret().getBytes());
@@ -68,11 +69,11 @@ public class IotDeviceTokenServiceImpl implements IotDeviceTokenService { @@ -68,11 +69,11 @@ public class IotDeviceTokenServiceImpl implements IotDeviceTokenService {
68 String deviceName = payload.getStr("deviceName"); 69 String deviceName = payload.getStr("deviceName");
69 Assert.notBlank(productKey, "productKey 不能为空"); 70 Assert.notBlank(productKey, "productKey 不能为空");
70 Assert.notBlank(deviceName, "deviceName 不能为空"); 71 Assert.notBlank(deviceName, "deviceName 不能为空");
71 - return new IotDeviceAuthUtils.DeviceInfo().setProductKey(productKey).setDeviceName(deviceName); 72 + return new IotDeviceIdentity(productKey, deviceName);
72 } 73 }
73 74
74 @Override 75 @Override
75 - public IotDeviceAuthUtils.DeviceInfo parseUsername(String username) { 76 + public IotDeviceIdentity parseUsername(String username) {
76 return IotDeviceAuthUtils.parseUsername(username); 77 return IotDeviceAuthUtils.parseUsername(username);
77 } 78 }
78 79
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java
@@ -6,9 +6,13 @@ import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; @@ -6,9 +6,13 @@ import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
6 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 6 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
7 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO; 7 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceGetReqDTO;
8 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; 8 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
  9 +import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO;
9 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; 10 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;
10 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; 11 import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO;
  12 +import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO;
11 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; 13 import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
  14 +
  15 +import java.util.List;
12 import jakarta.annotation.PostConstruct; 16 import jakarta.annotation.PostConstruct;
13 import jakarta.annotation.Resource; 17 import jakarta.annotation.Resource;
14 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
@@ -61,6 +65,11 @@ public class IotDeviceApiImpl implements IotDeviceCommonApi { @@ -61,6 +65,11 @@ public class IotDeviceApiImpl implements IotDeviceCommonApi {
61 return doPost("/register", reqDTO, new ParameterizedTypeReference<>() { }); 65 return doPost("/register", reqDTO, new ParameterizedTypeReference<>() { });
62 } 66 }
63 67
  68 + @Override
  69 + public CommonResult<List<IotSubDeviceRegisterRespDTO>> registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO) {
  70 + return doPost("/register-sub", reqDTO, new ParameterizedTypeReference<>() { });
  71 + }
  72 +
64 private <T, R> CommonResult<R> doPost(String url, T body, 73 private <T, R> CommonResult<R> doPost(String url, T body,
65 ParameterizedTypeReference<CommonResult<R>> responseType) { 74 ParameterizedTypeReference<CommonResult<R>> responseType) {
66 try { 75 try {
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotDeviceRegisterHttpProtocolIntegrationTest.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.protocol.http;  
2 -  
3 -import cn.hutool.core.map.MapUtil;  
4 -import cn.hutool.core.util.IdUtil;  
5 -import cn.hutool.http.HttpResponse;  
6 -import cn.hutool.http.HttpUtil;  
7 -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;  
8 -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;  
9 -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;  
10 -import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;  
11 -import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO;  
12 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;  
13 -import lombok.extern.slf4j.Slf4j;  
14 -import org.junit.jupiter.api.Test;  
15 -  
16 -import java.util.Collections;  
17 -  
18 -// TODO @AI:合并到 IotDirectDeviceHttpProtocolIntegrationTest 里呀,没必要拆开;只搞一个直连设备的注册就好了;  
19 -/**  
20 - * IoT 设备动态注册 HTTP 协议集成测试(手动测试)  
21 - *  
22 - * <p>测试场景:一型一密(One Type One Secret)动态注册机制  
23 - *  
24 - * <p><b>前置条件:</b>  
25 - * <ol>  
26 - * <li>产品已开启动态注册(registerEnabled = true)</li>  
27 - * <li>设备已预先创建(预注册模式)</li>  
28 - * <li>设备 deviceSecret 为空(未激活状态)</li>  
29 - * </ol>  
30 - *  
31 - * <p>使用步骤:  
32 - * <ol>  
33 - * <li>启动 yudao-module-iot-gateway 服务(HTTP 端口 8092)</li>  
34 - * <li>运行 {@link #testDeviceRegister()} 测试直连设备/网关动态注册</li>  
35 - * <li>运行 {@link #testSubDeviceRegister()} 测试子设备动态注册(需要先获取网关 Token)</li>  
36 - * </ol>  
37 - *  
38 - * @author 芋道源码  
39 - * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a>  
40 - */  
41 -@Slf4j  
42 -@SuppressWarnings("HttpUrlsUsage")  
43 -public class IotDeviceRegisterHttpProtocolIntegrationTest {  
44 -  
45 - private static final String SERVER_HOST = "127.0.0.1";  
46 - private static final int SERVER_PORT = 8092;  
47 -  
48 - // ===================== 直连设备/网关动态注册配置(根据实际情况修改) =====================  
49 - /**  
50 - * 产品 Key(需要开启动态注册)  
51 - */  
52 - private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT";  
53 - /**  
54 - * 产品密钥(从 iot_product 表的 product_secret 字段获取)  
55 - */  
56 - private static final String PRODUCT_SECRET = "your_product_secret";  
57 - /**  
58 - * 设备名称(需要预先创建,deviceSecret 为空)  
59 - */  
60 - private static final String DEVICE_NAME = "test-register-device";  
61 -  
62 - // ===================== 网关设备信息(用于子设备动态注册) =====================  
63 - private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v";  
64 - private static final String GATEWAY_DEVICE_NAME = "sub-ddd";  
65 - private static final String GATEWAY_DEVICE_SECRET = "b3d62c70f8a4495487ed1d35d61ac2b3";  
66 -  
67 - /**  
68 - * 网关设备 Token:从网关认证获取后,粘贴到这里  
69 - */  
70 - private static final String GATEWAY_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoibTZYY1MxWkozVFc4ZUMwdiIsImV4cCI6MTc2OTg2NjY3OCwiZGV2aWNlTmFtZSI6InN1Yi1kZGQifQ.nCLSAfHEjXLtTDRXARjOoFqpuo5WfArjFWweUAzrjKU";  
71 -  
72 - // ===================== 子设备信息(用于子设备动态注册) =====================  
73 - private static final String SUB_DEVICE_PRODUCT_KEY = "jAufEMTF1W6wnPhn";  
74 - private static final String SUB_DEVICE_NAME = "test-sub-register-device";  
75 -  
76 - // ===================== 直连设备/网关动态注册测试 =====================  
77 -  
78 - /**  
79 - * 直连设备/网关动态注册测试  
80 - * <p>  
81 - * 使用产品密钥(productSecret)进行签名验证,成功后返回设备密钥(deviceSecret)  
82 - * <p>  
83 - * 注意:此接口不需要 Token 认证  
84 - */  
85 - @Test  
86 - public void testDeviceRegister() {  
87 - // 1.1 构建请求  
88 - String url = String.format("http://%s:%d/auth/register/device", SERVER_HOST, SERVER_PORT);  
89 - // 1.2 生成签名  
90 - String random = IdUtil.fastSimpleUUID();  
91 - String sign = IotDeviceAuthUtils.buildRegisterSign(PRODUCT_SECRET, PRODUCT_KEY, DEVICE_NAME, random);  
92 - // 1.3 构建请求参数  
93 - IotDeviceRegisterReqDTO reqDTO = new IotDeviceRegisterReqDTO();  
94 - reqDTO.setProductKey(PRODUCT_KEY);  
95 - reqDTO.setDeviceName(DEVICE_NAME);  
96 - reqDTO.setRandom(random);  
97 - reqDTO.setSign(sign);  
98 - String payload = JsonUtils.toJsonString(reqDTO);  
99 - // 1.4 输出请求  
100 - log.info("[testDeviceRegister][请求 URL: {}]", url);  
101 - log.info("[testDeviceRegister][请求体: {}]", payload);  
102 -  
103 - // 2.1 发送请求  
104 - String response = HttpUtil.post(url, payload);  
105 - // 2.2 输出结果  
106 - log.info("[testDeviceRegister][响应体: {}]", response);  
107 - log.info("[testDeviceRegister][成功后可使用返回的 deviceSecret 进行一机一密认证]");  
108 - }  
109 -  
110 - /**  
111 - * 测试动态注册后使用 deviceSecret 进行认证  
112 - * <p>  
113 - * 此测试需要先执行 testDeviceRegister 获取 deviceSecret  
114 - */  
115 - @Test  
116 - public void testAuthAfterRegister() {  
117 - // 1.1 构建请求  
118 - String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT);  
119 - // TODO 将 testDeviceRegister 返回的 deviceSecret 填入此处  
120 - String deviceSecret = "返回的deviceSecret";  
121 - IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, deviceSecret);  
122 - IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()  
123 - .setClientId(authInfo.getClientId())  
124 - .setUsername(authInfo.getUsername())  
125 - .setPassword(authInfo.getPassword());  
126 - String payload = JsonUtils.toJsonString(authReqDTO);  
127 - // 1.2 输出请求  
128 - log.info("[testAuthAfterRegister][请求 URL: {}]", url);  
129 - log.info("[testAuthAfterRegister][请求体: {}]", payload);  
130 -  
131 - // 2.1 发送请求  
132 - String response = HttpUtil.post(url, payload);  
133 - // 2.2 输出结果  
134 - log.info("[testAuthAfterRegister][响应体: {}]", response);  
135 - }  
136 -  
137 - // ===================== 网关认证测试 =====================  
138 -  
139 - /**  
140 - * 网关设备认证测试:获取网关设备 Token(用于后续子设备动态注册)  
141 - */  
142 - @Test  
143 - public void testGatewayAuth() {  
144 - // 1.1 构建请求  
145 - String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT);  
146 - IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo(  
147 - GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET);  
148 - IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()  
149 - .setClientId(authInfo.getClientId())  
150 - .setUsername(authInfo.getUsername())  
151 - .setPassword(authInfo.getPassword());  
152 - String payload = JsonUtils.toJsonString(authReqDTO);  
153 - // 1.2 输出请求  
154 - log.info("[testGatewayAuth][请求 URL: {}]", url);  
155 - log.info("[testGatewayAuth][请求体: {}]", payload);  
156 -  
157 - // 2.1 发送请求  
158 - String response = HttpUtil.post(url, payload);  
159 - // 2.2 输出结果  
160 - log.info("[testGatewayAuth][响应体: {}]", response);  
161 - log.info("[testGatewayAuth][请将返回的 token 复制到 GATEWAY_TOKEN 常量中]");  
162 - }  
163 -  
164 - // ===================== 子设备动态注册测试 =====================  
165 -  
166 - /**  
167 - * 子设备动态注册测试  
168 - * <p>  
169 - * 网关设备代理子设备进行动态注册,平台返回子设备的 deviceSecret  
170 - * <p>  
171 - * 注意:此接口需要网关 Token 认证  
172 - */  
173 - @Test  
174 - public void testSubDeviceRegister() {  
175 - // 1.1 构建请求(需要网关认证)  
176 - String url = String.format("http://%s:%d/auth/register/sub-device/%s/%s",  
177 - SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);  
178 - // 1.2 构建请求参数  
179 - IotSubDeviceRegisterReqDTO subDevice = new IotSubDeviceRegisterReqDTO();  
180 - subDevice.setProductKey(SUB_DEVICE_PRODUCT_KEY);  
181 - subDevice.setDeviceName(SUB_DEVICE_NAME);  
182 - String payload = JsonUtils.toJsonString(MapUtil.builder()  
183 - .put("id", IdUtil.fastSimpleUUID())  
184 - .put("method", IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod())  
185 - .put("version", "1.0")  
186 - .put("params", Collections.singletonList(subDevice))  
187 - .build());  
188 - // 1.3 输出请求  
189 - log.info("[testSubDeviceRegister][请求 URL: {}]", url);  
190 - log.info("[testSubDeviceRegister][请求体: {}]", payload);  
191 -  
192 - // 2.1 发送请求(需要网关 Token)  
193 - try (HttpResponse httpResponse = HttpUtil.createPost(url)  
194 - .header("Authorization", GATEWAY_TOKEN)  
195 - .body(payload)  
196 - .execute()) {  
197 - // 2.2 输出结果  
198 - log.info("[testSubDeviceRegister][响应体: {}]", httpResponse.body());  
199 - log.info("[testSubDeviceRegister][成功后可使用返回的 deviceSecret 进行子设备认证]");  
200 - }  
201 - }  
202 -  
203 -}  
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotDirectDeviceHttpProtocolIntegrationTest.java
@@ -7,6 +7,7 @@ import cn.hutool.http.HttpUtil; @@ -7,6 +7,7 @@ import cn.hutool.http.HttpUtil;
7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
8 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; 8 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO;
9 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; 9 import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
  10 +import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO;
10 import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO; 11 import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;
11 import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO; 12 import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;
12 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 13 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
@@ -22,6 +23,7 @@ import org.junit.jupiter.api.Test; @@ -22,6 +23,7 @@ import org.junit.jupiter.api.Test;
22 * <p>使用步骤: 23 * <p>使用步骤:
23 * <ol> 24 * <ol>
24 * <li>启动 yudao-module-iot-gateway 服务(HTTP 端口 8092)</li> 25 * <li>启动 yudao-module-iot-gateway 服务(HTTP 端口 8092)</li>
  26 + * <li>运行 {@link #testDeviceRegister()} 测试直连设备动态注册(一型一密)</li>
25 * <li>运行 {@link #testAuth()} 获取设备 token,将返回的 token 粘贴到 {@link #TOKEN} 常量</li> 27 * <li>运行 {@link #testAuth()} 获取设备 token,将返回的 token 粘贴到 {@link #TOKEN} 常量</li>
26 * <li>运行以下测试方法: 28 * <li>运行以下测试方法:
27 * <ul> 29 * <ul>
@@ -46,10 +48,77 @@ public class IotDirectDeviceHttpProtocolIntegrationTest { @@ -46,10 +48,77 @@ public class IotDirectDeviceHttpProtocolIntegrationTest {
46 private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3"; 48 private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3";
47 49
48 /** 50 /**
  51 + * 产品密钥(从 iot_product 表的 product_secret 字段获取),用于动态注册
  52 + */
  53 + private static final String PRODUCT_SECRET = "your_product_secret";
  54 +
  55 + /**
  56 + * 动态注册的设备名称
  57 + */
  58 + private static final String REGISTER_DEVICE_NAME = "test-register-device";
  59 +
  60 + /**
49 * 直连设备 Token:从 {@link #testAuth()} 方法获取后,粘贴到这里 61 * 直连设备 Token:从 {@link #testAuth()} 方法获取后,粘贴到这里
50 */ 62 */
51 private static final String TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoiNGF5bVpnT1RPT0NyREtSVCIsImV4cCI6MTc2OTMwNTA1NSwiZGV2aWNlTmFtZSI6InNtYWxsIn0.mf3MEATCn5bp6cXgULunZjs8d00RGUxj96JEz0hMS7k"; 63 private static final String TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoiNGF5bVpnT1RPT0NyREtSVCIsImV4cCI6MTc2OTMwNTA1NSwiZGV2aWNlTmFtZSI6InNtYWxsIn0.mf3MEATCn5bp6cXgULunZjs8d00RGUxj96JEz0hMS7k";
52 64
  65 + // ===================== 动态注册测试 =====================
  66 +
  67 + /**
  68 + * 直连设备动态注册测试(一型一密)
  69 + * <p>
  70 + * 使用产品密钥(productSecret)验证身份,成功后返回设备密钥(deviceSecret)
  71 + * <p>
  72 + * 注意:此接口不需要 Token 认证
  73 + */
  74 + @Test
  75 + public void testDeviceRegister() {
  76 + // 1.1 构建请求
  77 + String url = String.format("http://%s:%d/auth/register/device", SERVER_HOST, SERVER_PORT);
  78 + // 1.2 构建请求参数
  79 + IotDeviceRegisterReqDTO reqDTO = new IotDeviceRegisterReqDTO();
  80 + reqDTO.setProductKey(PRODUCT_KEY);
  81 + reqDTO.setDeviceName(REGISTER_DEVICE_NAME);
  82 + reqDTO.setProductSecret(PRODUCT_SECRET);
  83 + String payload = JsonUtils.toJsonString(reqDTO);
  84 + // 1.3 输出请求
  85 + log.info("[testDeviceRegister][请求 URL: {}]", url);
  86 + log.info("[testDeviceRegister][请求体: {}]", payload);
  87 +
  88 + // 2.1 发送请求
  89 + String response = HttpUtil.post(url, payload);
  90 + // 2.2 输出结果
  91 + log.info("[testDeviceRegister][响应体: {}]", response);
  92 + log.info("[testDeviceRegister][成功后可使用返回的 deviceSecret 进行一机一密认证]");
  93 + }
  94 +
  95 + /**
  96 + * 测试动态注册后使用 deviceSecret 进行认证
  97 + * <p>
  98 + * 此测试需要先执行 testDeviceRegister 获取 deviceSecret
  99 + */
  100 + @Test
  101 + public void testAuthAfterRegister() {
  102 + // 1.1 构建请求
  103 + String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT);
  104 + // TODO 将 testDeviceRegister 返回的 deviceSecret 填入此处
  105 + String deviceSecret = "返回的deviceSecret";
  106 + IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, REGISTER_DEVICE_NAME, deviceSecret);
  107 + IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()
  108 + .setClientId(authInfo.getClientId())
  109 + .setUsername(authInfo.getUsername())
  110 + .setPassword(authInfo.getPassword());
  111 + String payload = JsonUtils.toJsonString(authReqDTO);
  112 + // 1.2 输出请求
  113 + log.info("[testAuthAfterRegister][请求 URL: {}]", url);
  114 + log.info("[testAuthAfterRegister][请求体: {}]", payload);
  115 +
  116 + // 2.1 发送请求
  117 + String response = HttpUtil.post(url, payload);
  118 + // 2.2 输出结果
  119 + log.info("[testAuthAfterRegister][响应体: {}]", response);
  120 + }
  121 +
53 // ===================== 认证测试 ===================== 122 // ===================== 认证测试 =====================
54 123
55 /** 124 /**
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotGatewayDeviceHttpProtocolIntegrationTest.java
@@ -75,7 +75,7 @@ public class IotGatewayDeviceHttpProtocolIntegrationTest { @@ -75,7 +75,7 @@ public class IotGatewayDeviceHttpProtocolIntegrationTest {
75 public void testAuth() { 75 public void testAuth() {
76 // 1.1 构建请求 76 // 1.1 构建请求
77 String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT); 77 String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT);
78 - IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo( 78 + IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(
79 GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET); 79 GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET);
80 IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO() 80 IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()
81 .setClientId(authInfo.getClientId()) 81 .setClientId(authInfo.getClientId())
@@ -108,7 +108,7 @@ public class IotGatewayDeviceHttpProtocolIntegrationTest { @@ -108,7 +108,7 @@ public class IotGatewayDeviceHttpProtocolIntegrationTest {
108 String url = String.format("http://%s:%d/topic/sys/%s/%s/thing/topo/add", 108 String url = String.format("http://%s:%d/topic/sys/%s/%s/thing/topo/add",
109 SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME); 109 SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME);
110 // 1.2 构建子设备认证信息 110 // 1.2 构建子设备认证信息
111 - IotDeviceAuthUtils.AuthInfo subAuthInfo = IotDeviceAuthUtils.getAuthInfo( 111 + IotDeviceAuthReqDTO subAuthInfo = IotDeviceAuthUtils.getAuthInfo(
112 SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME, SUB_DEVICE_SECRET); 112 SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME, SUB_DEVICE_SECRET);
113 IotDeviceAuthReqDTO subDeviceAuth = new IotDeviceAuthReqDTO() 113 IotDeviceAuthReqDTO subDeviceAuth = new IotDeviceAuthReqDTO()
114 .setClientId(subAuthInfo.getClientId()) 114 .setClientId(subAuthInfo.getClientId())
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotGatewaySubDeviceHttpProtocolIntegrationTest.java
@@ -63,7 +63,7 @@ public class IotGatewaySubDeviceHttpProtocolIntegrationTest { @@ -63,7 +63,7 @@ public class IotGatewaySubDeviceHttpProtocolIntegrationTest {
63 public void testAuth() { 63 public void testAuth() {
64 // 1.1 构建请求 64 // 1.1 构建请求
65 String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT); 65 String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT);
66 - IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET); 66 + IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET);
67 IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO() 67 IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO()
68 .setClientId(authInfo.getClientId()) 68 .setClientId(authInfo.getClientId())
69 .setUsername(authInfo.getUsername()) 69 .setUsername(authInfo.getUsername())