diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java index 71b779681e..db0a862d0e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java +++ b/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; 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.biz.dto.IotSubDeviceRegisterFullReqDTO; import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; +import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; @@ -21,6 +23,8 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; /** @@ -66,4 +70,11 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { return success(deviceService.registerDevice(reqDTO)); } + @Override + @PostMapping(RpcConstants.RPC_API_PREFIX + "/iot/device/register-sub") + @PermitAll + public CommonResult> registerSubDevices(@RequestBody IotSubDeviceRegisterFullReqDTO reqDTO) { + return success(deviceService.registerSubDevices(reqDTO)); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java index 85a151f961..08c636f7f2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java +++ b/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 { private String codecType; @Schema(description = "是否开启动态注册", example = "false") - @NotEmpty(message = "是否开启动态注册不能为空") + @NotNull(message = "是否开启动态注册不能为空") private Boolean registerEnabled; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 44ead7bccd..3679dbf1ce 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/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 { ErrorCode DEVICE_SUB_REGISTER_PARAMS_INVALID = new ErrorCode(1_050_003_200, "子设备注册参数无效"); ErrorCode DEVICE_SUB_REGISTER_PRODUCT_NOT_GATEWAY_SUB = new ErrorCode(1_050_003_201, "产品【{}】不是网关子设备类型"); ErrorCode DEVICE_REGISTER_DISABLED = new ErrorCode(1_050_003_210, "该产品未开启动态注册功能"); - ErrorCode DEVICE_REGISTER_SIGN_INVALID = new ErrorCode(1_050_003_211, "动态注册签名验证失败"); - ErrorCode DEVICE_ALREADY_ACTIVATED = new ErrorCode(1_050_003_212, "设备已激活,不允许重复注册"); + ErrorCode DEVICE_REGISTER_SECRET_INVALID = new ErrorCode(1_050_003_211, "产品密钥验证失败"); + ErrorCode DEVICE_REGISTER_ALREADY_EXISTS = new ErrorCode(1_050_003_212, "设备已存在,不允许重复注册"); // ========== 产品分类 1-050-004-000 ========== ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在"); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index fa5dcd3351..5a622e5654 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/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; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; @@ -44,18 +45,6 @@ public interface IotDeviceService { */ void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO); - // TODO @芋艿:先这么实现。未来看情况,要不要自己实现 - - /** - * 更新设备的所属网关 - * - * @param id 编号 - * @param gatewayId 网关设备 ID - */ - default void updateDeviceGateway(Long id, Long gatewayId) { - updateDevice(new IotDeviceSaveReqVO().setId(id).setGatewayId(gatewayId)); - } - /** * 更新设备状态 * @@ -358,6 +347,25 @@ public interface IotDeviceService { // ========== 设备动态注册 ========== + /** + * 直连/网关设备动态注册 + * + * @param reqDTO 动态注册请求 + * @return 注册结果(包含 DeviceSecret) + */ + IotDeviceRegisterRespDTO registerDevice(@Valid IotDeviceRegisterReqDTO reqDTO); + + /** + * 网关子设备动态注册 + *

+ * 与 {@link #handleSubDeviceRegisterMessage} 方法的区别: + * 该方法网关设备信息通过 reqDTO 参数传入,而 {@link #handleSubDeviceRegisterMessage} 方法通过 gatewayDevice 参数传入 + * + * @param reqDTO 子设备注册请求(包含网关设备信息) + * @return 注册结果列表 + */ + List registerSubDevices(@Valid IotSubDeviceRegisterFullReqDTO reqDTO); + /** * 处理子设备动态注册消息(网关设备上报) * @@ -367,12 +375,4 @@ public interface IotDeviceService { */ List handleSubDeviceRegisterMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice); - /** - * 设备动态注册(直连设备/网关) - * - * @param reqDTO 动态注册请求 - * @return 注册结果(包含 DeviceSecret) - */ - IotDeviceRegisterRespDTO registerDevice(@Valid IotDeviceRegisterReqDTO reqDTO); - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 5602f17e1a..03b82b6417 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/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; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; @@ -14,6 +15,7 @@ import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; @@ -142,11 +144,13 @@ public class IotDeviceServiceImpl implements IotDeviceService { private void initDevice(IotDeviceDO device, IotProductDO product) { device.setProductId(product.getId()).setProductKey(product.getProductKey()) - .setDeviceType(product.getDeviceType()); - // 生成密钥 - device.setDeviceSecret(IotDeviceAuthUtils.generateDeviceSecret()); - // 设置设备状态为未激活 - device.setState(IotDeviceStateEnum.INACTIVE.getState()); + .setDeviceType(product.getDeviceType()) + .setDeviceSecret(generateDeviceSecret()) // 生成密钥 + .setState(IotDeviceStateEnum.INACTIVE.getState()); // 默认未激活 + } + + private String generateDeviceSecret() { + return IdUtil.fastSimpleUUID(); } @Override @@ -448,7 +452,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { public IotDeviceAuthInfoRespVO getDeviceAuthInfo(Long id) { IotDeviceDO device = validateDeviceExists(id); // 使用 IotDeviceAuthUtils 生成认证信息 - IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo( + IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo( device.getProductKey(), device.getDeviceName(), device.getDeviceSecret()); return BeanUtils.toBean(authInfo, IotDeviceAuthInfoRespVO.class); } @@ -496,7 +500,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { @Override public boolean authDevice(IotDeviceAuthReqDTO authReqDTO) { // 1. 校验设备是否存在 - IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(authReqDTO.getUsername()); + IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(authReqDTO.getUsername()); if (deviceInfo == null) { log.error("[authDevice][认证失败,username({}) 格式不正确]", authReqDTO.getUsername()); return false; @@ -510,7 +514,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { } // 2. 校验密码 - IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo(productKey, deviceName, device.getDeviceSecret()); + IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(productKey, deviceName, device.getDeviceSecret()); if (ObjUtil.notEqual(authInfo.getPassword(), authReqDTO.getPassword())) { log.error("[authDevice][设备({}/{}) 密码不正确]", productKey, deviceName); return false; @@ -666,7 +670,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { private IotDeviceDO addDeviceTopo(IotDeviceDO gatewayDevice, IotDeviceAuthReqDTO subDeviceAuth) { // 1.1 解析子设备信息 - IotDeviceAuthUtils.DeviceInfo subDeviceInfo = IotDeviceAuthUtils.parseUsername(subDeviceAuth.getUsername()); + IotDeviceIdentity subDeviceInfo = IotDeviceAuthUtils.parseUsername(subDeviceAuth.getUsername()); if (subDeviceInfo == null) { throw exception(DEVICE_TOPO_SUB_DEVICE_USERNAME_INVALID); } @@ -814,63 +818,78 @@ public class IotDeviceServiceImpl implements IotDeviceService { if (BooleanUtil.isFalse(product.getRegisterEnabled())) { throw exception(DEVICE_REGISTER_DISABLED); } - // 1.3 验证签名 - if (!IotDeviceAuthUtils.verifyRegisterSign(product.getProductSecret(), - reqDTO.getProductKey(), reqDTO.getDeviceName(), reqDTO.getRandom(), reqDTO.getSign())) { - throw exception(DEVICE_REGISTER_SIGN_INVALID); + // 1.3 验证 productSecret + if (ObjUtil.notEqual(product.getProductSecret(), reqDTO.getProductSecret())) { + throw exception(DEVICE_REGISTER_SECRET_INVALID); } - - // 4. 查找设备(预注册模式:设备必须已存在) - // TODO @AI:设备不用提前有,这个有问题! + // 1.4 校验设备是否已存在(已存在则不允许重复注册) IotDeviceDO device = getSelf().getDeviceFromCache(reqDTO.getProductKey(), reqDTO.getDeviceName()); - if (device == null) { - throw exception(DEVICE_NOT_EXISTS); + if (device != null) { + throw exception(DEVICE_REGISTER_ALREADY_EXISTS); } - // 5. 校验设备是否已激活(已激活的设备不允许重复注册) - if (!Objects.equals(device.getState(), IotDeviceStateEnum.INACTIVE.getState())) { - throw exception(DEVICE_ALREADY_ACTIVATED); - } - - // 6. 返回设备密钥 + // 2.1 自动创建设备 + IotDeviceSaveReqVO createReqVO = new IotDeviceSaveReqVO() + .setDeviceName(reqDTO.getDeviceName()) + .setProductId(product.getId()); + device = createDevice0(createReqVO); + log.info("[registerDevice][产品({}) 自动创建设备({})]", + reqDTO.getProductKey(), reqDTO.getDeviceName()); + // 2.2 返回设备密钥 return new IotDeviceRegisterRespDTO(device.getProductKey(), device.getDeviceName(), device.getDeviceSecret()); } + @Override + public List registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO) { + // 1. 校验网关设备 + IotDeviceDO gatewayDevice = getSelf().getDeviceFromCache(reqDTO.getGatewayProductKey(), reqDTO.getGatewayDeviceName()); + + // 2. 遍历注册每个子设备 + return registerSubDevices0(gatewayDevice, reqDTO.getSubDevices()); + } + @Override public List handleSubDeviceRegisterMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice) { - // 1.1 校验网关设备类型 - if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) { - throw exception(DEVICE_NOT_GATEWAY); - } - // 1.2 解析参数 + // 1. 解析参数 if (!(message.getParams() instanceof List)) { throw exception(DEVICE_SUB_REGISTER_PARAMS_INVALID); } - List paramsList = JsonUtils.convertList(message.getParams(), - IotSubDeviceRegisterReqDTO.class); - if (CollUtil.isEmpty(paramsList)) { + List subDevices = JsonUtils.convertList(message.getParams(), IotSubDeviceRegisterReqDTO.class); + + // 2. 遍历注册每个子设备 + return registerSubDevices0(gatewayDevice, subDevices); + } + + private List registerSubDevices0(IotDeviceDO gatewayDevice, + List subDevices) { + // 1.1 校验网关设备 + if (gatewayDevice == null) { + throw exception(DEVICE_NOT_EXISTS); + } + if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) { + throw exception(DEVICE_NOT_GATEWAY); + } + // 1.2 注册设备不能为空 + if (CollUtil.isEmpty(subDevices)) { throw exception(DEVICE_SUB_REGISTER_PARAMS_INVALID); } // 2. 遍历注册每个子设备 - List results = new ArrayList<>(paramsList.size()); - for (IotSubDeviceRegisterReqDTO params : paramsList) { + List results = new ArrayList<>(subDevices.size()); + for (IotSubDeviceRegisterReqDTO subDevice : subDevices) { try { - IotDeviceDO device = registerSubDevice(gatewayDevice, params); + IotDeviceDO device = registerSubDevice0(gatewayDevice, subDevice); results.add(new IotSubDeviceRegisterRespDTO( - params.getProductKey(), params.getDeviceName(), device.getDeviceSecret())); + subDevice.getProductKey(), subDevice.getDeviceName(), device.getDeviceSecret())); } catch (Exception ex) { - log.error("[handleSubDeviceRegisterMessage][子设备({}/{}) 注册失败]", - params.getProductKey(), params.getDeviceName(), ex); + log.error("[registerSubDevices0][子设备({}/{}) 注册失败]", + subDevice.getProductKey(), subDevice.getDeviceName(), ex); } } - - // 3. 返回响应数据(包含成功注册的子设备列表) return results; } - // TODO @AI:阿里云的,设备必须存在; - private IotDeviceDO registerSubDevice(IotDeviceDO gatewayDevice, IotSubDeviceRegisterReqDTO params) { + private IotDeviceDO registerSubDevice0(IotDeviceDO gatewayDevice, IotSubDeviceRegisterReqDTO params) { // 1.1 校验产品 IotProductDO product = productService.getProductByProductKey(params.getProductKey()); if (product == null) { @@ -880,28 +899,28 @@ public class IotDeviceServiceImpl implements IotDeviceService { if (!IotProductDeviceTypeEnum.isGatewaySub(product.getDeviceType())) { throw exception(DEVICE_SUB_REGISTER_PRODUCT_NOT_GATEWAY_SUB, params.getProductKey()); } - // 1.3 查找设备是否已存在 + // 1.3 校验设备是否已存在(子设备动态注册:设备必须已预注册) IotDeviceDO existDevice = getSelf().getDeviceFromCache(params.getProductKey(), params.getDeviceName()); - if (existDevice != null) { - // 校验是否绑定到当前网关 - if (ObjUtil.notEqual(existDevice.getGatewayId(), gatewayDevice.getId())) { - throw exception(DEVICE_GATEWAY_BINDTO_EXISTS, - existDevice.getProductKey(), existDevice.getDeviceName()); - } - // 已存在则返回设备信息 - return existDevice; + if (existDevice == null) { + throw exception(DEVICE_NOT_EXISTS); + } + // 1.4 校验是否绑定到其他网关 + if (existDevice.getGatewayId() != null && ObjUtil.notEqual(existDevice.getGatewayId(), gatewayDevice.getId())) { + throw exception(DEVICE_GATEWAY_BINDTO_EXISTS, + existDevice.getProductKey(), existDevice.getDeviceName()); } - // 2. 创建新设备 - IotDeviceSaveReqVO createReqVO = new IotDeviceSaveReqVO() - .setDeviceName(params.getDeviceName()) - .setProductId(product.getId()) - .setGatewayId(gatewayDevice.getId()); - IotDeviceDO newDevice = createDevice0(createReqVO); - log.info("[registerSubDevice][网关({}/{}) 注册子设备({}/{})]", - gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), - newDevice.getProductKey(), newDevice.getDeviceName()); - return newDevice; + // 2. 绑定到网关(如果尚未绑定) + if (existDevice.getGatewayId() == null) { + // 2.1 更新数据库 + deviceMapper.updateById(new IotDeviceDO().setId(existDevice.getId()).setGatewayId(gatewayDevice.getId())); + // 2.2 清空对应缓存 + deleteDeviceCache(existDevice); + log.info("[registerSubDevice][网关({}/{}) 绑定子设备({}/{})]", + gatewayDevice.getProductKey(), gatewayDevice.getDeviceName(), + existDevice.getProductKey(), existDevice.getDeviceName()); + } + return existDevice; } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index f7195f6715..0e952336ca 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/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; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; +import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; @@ -55,11 +55,15 @@ public class IotProductServiceImpl implements IotProductService { // 2. 插入 IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class) .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus()) - .setProductSecret(IotDeviceAuthUtils.generateProductSecret()); + .setProductSecret(generateProductSecret()); productMapper.insert(product); return product.getId(); } + private String generateProductSecret() { + return IdUtil.fastSimpleUUID(); + } + @Override @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#updateReqVO.id") public void updateProduct(IotProductSaveReqVO updateReqVO) { diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/IotDeviceCommonApi.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/IotDeviceCommonApi.java index c86c429e03..cc0cb071a1 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/IotDeviceCommonApi.java +++ b/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; 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.biz.dto.IotSubDeviceRegisterFullReqDTO; import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; +import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; + +import java.util.List; /** * IoT 设备通用 API @@ -31,11 +35,19 @@ public interface IotDeviceCommonApi { CommonResult getDevice(IotDeviceGetReqDTO infoReqDTO); /** - * 设备动态注册(一型一密) + * 直连/网关设备动态注册(一型一密) * * @param reqDTO 动态注册请求 * @return 注册结果(包含 DeviceSecret) */ CommonResult registerDevice(IotDeviceRegisterReqDTO reqDTO); + /** + * 网关子设备动态注册(网关代理转发) + * + * @param reqDTO 子设备注册请求(包含网关标识和子设备列表) + * @return 注册结果列表 + */ + CommonResult> registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO); + } diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceAuthReqDTO.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceAuthReqDTO.java index 9e62a2fc0c..2f25fb4964 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceAuthReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceAuthReqDTO.java @@ -1,7 +1,9 @@ package cn.iocoder.yudao.module.iot.core.biz.dto; import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; /** * IoT 设备认证 Request DTO @@ -9,6 +11,8 @@ import lombok.Data; * @author 芋道源码 */ @Data +@NoArgsConstructor +@AllArgsConstructor public class IotDeviceAuthReqDTO { /** diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotSubDeviceRegisterFullReqDTO.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotSubDeviceRegisterFullReqDTO.java new file mode 100644 index 0000000000..76bf5ffb3f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotSubDeviceRegisterFullReqDTO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.iot.core.biz.dto; + +import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * IoT 子设备动态注册 Request DTO + *

+ * 额外包含了网关设备的标识信息 + * + * @author 芋道源码 + */ +@Data +public class IotSubDeviceRegisterFullReqDTO { + + /** + * 网关设备 ProductKey + */ + @NotEmpty(message = "网关产品标识不能为空") + private String gatewayProductKey; + + /** + * 网关设备 DeviceName + */ + @NotEmpty(message = "网关设备名称不能为空") + private String gatewayDeviceName; + + /** + * 子设备注册列表 + */ + @NotNull(message = "子设备注册列表不能为空") + private List subDevices; + +} diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotDeviceRegisterReqDTO.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotDeviceRegisterReqDTO.java index 1b2c86f6ef..b8db15f188 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/auth/IotDeviceRegisterReqDTO.java +++ b/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; /** * IoT 设备动态注册 Request DTO *

- * 用于直连设备/网关的一型一密动态注册:使用 ProductSecret 验证签名,返回 DeviceSecret + * 用于直连设备/网关的一型一密动态注册:使用 productSecret 验证,返回 deviceSecret * * @author 芋道源码 * @see 阿里云 - 一型一密 @@ -26,18 +26,10 @@ public class IotDeviceRegisterReqDTO { @NotEmpty(message = "设备名称不能为空") private String deviceName; - // TODO @AI:可以去掉 random 字段; /** - * 随机数,用于签名 + * 产品密钥 */ - @NotEmpty(message = "随机数不能为空") - private String random; - - // TODO @AI:看起来,是直接带 productSecret 阿里云上,你在检查下! - /** - * 签名 - */ - @NotEmpty(message = "签名不能为空") - private String sign; + @NotEmpty(message = "产品密钥不能为空") + private String productSecret; } diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/event/IotDeviceEventPostReqDTO.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/event/IotDeviceEventPostReqDTO.java index 01451506d6..3b6a7a7d4c 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/event/IotDeviceEventPostReqDTO.java +++ b/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; * 用于 thing.event.post 消息的 params 参数 * * @author 芋道源码 - * @see 阿里云 - 设备上报事件 + * @see 阿里云 - 设备上报事件 */ @Data public class IotDeviceEventPostReqDTO { diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/property/IotDevicePropertyPostReqDTO.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/property/IotDevicePropertyPostReqDTO.java index 4adc2f8d4b..2e537442d7 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/topic/property/IotDevicePropertyPostReqDTO.java +++ b/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; * 本质是一个 Map,key 为属性标识符,value 为属性值 * * @author 芋道源码 - * @see 阿里云 - 设备上报属性 + * @see 阿里云 - 设备上报属性 */ public class IotDevicePropertyPostReqDTO extends HashMap { diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceAuthUtils.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceAuthUtils.java index df1bbbbffe..609d0a60ae 100644 --- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceAuthUtils.java +++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceAuthUtils.java @@ -1,12 +1,10 @@ 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; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; /** * IoT 设备【认证】的工具类,参考阿里云 @@ -15,49 +13,12 @@ import lombok.NoArgsConstructor; */ public class IotDeviceAuthUtils { - /** - * 认证信息 - */ - @Data - @NoArgsConstructor - @AllArgsConstructor - public static class AuthInfo { - - /** - * 客户端 ID - */ - private String clientId; - - /** - * 用户名 - */ - private String username; - - /** - * 密码 - */ - private String password; - - } - - /** - * 设备信息 - */ - @Data - public static class DeviceInfo { - - private String productKey; - - private String deviceName; - - } - - public static AuthInfo getAuthInfo(String productKey, String deviceName, String deviceSecret) { + public static IotDeviceAuthReqDTO getAuthInfo(String productKey, String deviceName, String deviceSecret) { String clientId = buildClientId(productKey, deviceName); String username = buildUsername(productKey, deviceName); String password = buildPassword(deviceSecret, buildContent(clientId, productKey, deviceName, deviceSecret)); - return new AuthInfo(clientId, username, password); + return new IotDeviceAuthReqDTO(clientId, username, password); } public static String buildClientId(String productKey, String deviceName) { @@ -80,70 +41,12 @@ public class IotDeviceAuthUtils { "productKey" + productKey; } - public static DeviceInfo parseUsername(String username) { + public static IotDeviceIdentity parseUsername(String username) { String[] usernameParts = username.split("&"); if (usernameParts.length != 2) { return null; } - 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; - /** - * 计算动态注册签名 - *

- * 参考阿里云规范,参数按字典序排列拼接 - * - * @param productSecret 产品密钥 - * @param productKey 产品标识 - * @param deviceName 设备名称 - * @param random 随机数 - * @return 签名 - * @see 一型一密 - */ - 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); + return new IotDeviceIdentity(usernameParts[1], usernameParts[0]); } } diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/router/IotEmqxAuthEventHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/router/IotEmqxAuthEventHandler.java index d6957bd52f..3395d5c8ae 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/router/IotEmqxAuthEventHandler.java +++ b/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; import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; import io.vertx.core.json.JsonObject; @@ -201,7 +202,7 @@ public class IotEmqxAuthEventHandler { */ private void handleDeviceStateChange(String username, boolean online) { // 1. 解析设备信息 - IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(username); + IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username); if (deviceInfo == null) { log.debug("[handleDeviceStateChange][跳过非设备({})连接]", username); return; diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpUpstreamProtocol.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpUpstreamProtocol.java index 22826062b8..a9ba930f1d 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotHttpUpstreamProtocol.java +++ b/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 { router.post(IotHttpAuthHandler.PATH).handler(authHandler); IotHttpRegisterHandler registerHandler = new IotHttpRegisterHandler(); router.post(IotHttpRegisterHandler.PATH).handler(registerHandler); - IotHttpRegisterSubHandler registerSubHandler = new IotHttpRegisterSubHandler(this); + IotHttpRegisterSubHandler registerSubHandler = new IotHttpRegisterSubHandler(); router.post(IotHttpRegisterSubHandler.PATH).handler(registerSubHandler); IotHttpUpstreamHandler upstreamHandler = new IotHttpUpstreamHandler(this); router.post(IotHttpUpstreamHandler.PATH).handler(upstreamHandler); diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAbstractHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAbstractHandler.java index 883e3239b7..850fde1878 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAbstractHandler.java +++ b/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; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; import io.vertx.core.Handler; import io.vertx.core.http.HttpHeaders; @@ -74,7 +74,7 @@ public abstract class IotHttpAbstractHandler implements Handler } // 校验 token - IotDeviceAuthUtils.DeviceInfo deviceInfo = deviceTokenService.verifyToken(token); + IotDeviceIdentity deviceInfo = deviceTokenService.verifyToken(token); Assert.notNull(deviceInfo, "设备信息不能为空"); // 校验设备信息是否匹配 if (ObjUtil.notEqual(productKey, deviceInfo.getProductKey()) diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAuthHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAuthHandler.java index e6a52cdf0f..c6a9331ab6 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAuthHandler.java +++ b/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; import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; @@ -72,7 +72,7 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { throw exception(DEVICE_AUTH_FAIL); } // 2.2 生成 Token - IotDeviceAuthUtils.DeviceInfo deviceInfo = deviceTokenService.parseUsername(username); + IotDeviceIdentity deviceInfo = deviceTokenService.parseUsername(username); Assert.notNull(deviceInfo, "设备信息不能为空"); String token = deviceTokenService.createToken(deviceInfo.getProductKey(), deviceInfo.getDeviceName()); Assert.notBlank(token, "生成 token 不能为空位"); diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpRegisterHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpRegisterHandler.java index 56a02d730c..525bd8487e 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpRegisterHandler.java +++ b/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 { @Override public CommonResult handle0(RoutingContext context) { // 1. 解析参数 - // TODO @AI:参数不太对,看看我写的建议 JsonObject body = context.body().asJsonObject(); String productKey = body.getString("productKey"); if (StrUtil.isEmpty(productKey)) { @@ -43,21 +42,14 @@ public class IotHttpRegisterHandler extends IotHttpAbstractHandler { if (StrUtil.isEmpty(deviceName)) { throw invalidParamException("deviceName 不能为空"); } - String random = body.getString("random"); - if (StrUtil.isEmpty(random)) { - throw invalidParamException("random 不能为空"); - } - String sign = body.getString("sign"); - if (StrUtil.isEmpty(sign)) { - throw invalidParamException("sign 不能为空"); + String productSecret = body.getString("productSecret"); + if (StrUtil.isEmpty(productSecret)) { + throw invalidParamException("productSecret 不能为空"); } // 2. 调用动态注册 IotDeviceRegisterReqDTO reqDTO = new IotDeviceRegisterReqDTO() - .setProductKey(productKey) - .setDeviceName(deviceName) - .setRandom(random) - .setSign(sign); + .setProductKey(productKey).setDeviceName(deviceName).setProductSecret(productSecret); CommonResult result = deviceApi.registerDevice(reqDTO); result.checkError(); diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpRegisterSubHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpRegisterSubHandler.java index e4ace04451..04aad65128 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpRegisterSubHandler.java +++ b/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; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; -import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; -import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi; +import cn.iocoder.yudao.module.iot.core.biz.dto.IotSubDeviceRegisterFullReqDTO; +import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; +import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; +import java.util.List; + import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; /** @@ -27,13 +30,10 @@ public class IotHttpRegisterSubHandler extends IotHttpAbstractHandler { */ public static final String PATH = "/auth/register/sub-device/:productKey/:deviceName"; - private final IotHttpUpstreamProtocol protocol; + private final IotDeviceCommonApi deviceApi; - private final IotDeviceMessageService deviceMessageService; - - public IotHttpRegisterSubHandler(IotHttpUpstreamProtocol protocol) { - this.protocol = protocol; - this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class); + public IotHttpRegisterSubHandler() { + this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class); } @Override @@ -42,18 +42,19 @@ public class IotHttpRegisterSubHandler extends IotHttpAbstractHandler { String productKey = context.pathParam("productKey"); String deviceName = context.pathParam("deviceName"); - // 2.1 解析消息 - byte[] bytes = context.body().buffer().getBytes(); - IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(bytes, productKey, deviceName); - // 2.2 设置方法 - message.setMethod(IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod()); + // 2. 解析子设备列表 + JsonObject body = context.body().asJsonObject(); + List subDevices = JsonUtils.parseArray( + body.getJsonArray("params").toString(), cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO.class); - // TODO @AI:可能还是需要一个新的 deviceApi 接口。因为 register sub 子设备不太一行; - // 2.3 发送消息 - Object responseData = deviceMessageService.sendDeviceMessage(message, productKey, deviceName, protocol.getServerId()); + // 3. 调用子设备动态注册 + IotSubDeviceRegisterFullReqDTO reqDTO = new IotSubDeviceRegisterFullReqDTO() + .setGatewayProductKey(productKey).setGatewayDeviceName(deviceName).setSubDevices(subDevices); + CommonResult> result = deviceApi.registerSubDevices(reqDTO); + result.checkError(); - // 3. 返回结果 - return success(responseData); + // 4. 返回结果 + return success(result.getData()); } } diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttUpstreamHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttUpstreamHandler.java index 4c0eb6e612..7c3d1a627a 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/IotMqttUpstreamHandler.java +++ b/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; 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.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.IotMqttUpstreamProtocol; import cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.manager.IotMqttConnectionManager; @@ -214,7 +215,7 @@ public class IotMqttUpstreamHandler { } // 4. 获取设备信息 - IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(username); + IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username); if (deviceInfo == null) { log.warn("[authenticateDevice][用户名格式不正确,客户端 ID: {},用户名: {}]", clientId, username); return false; diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqttws/router/IotMqttWsUpstreamHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqttws/router/IotMqttWsUpstreamHandler.java index d11d109502..26833fb46f 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqttws/router/IotMqttWsUpstreamHandler.java +++ b/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; 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.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.IotMqttWsUpstreamProtocol; import cn.iocoder.yudao.module.iot.gateway.protocol.mqttws.manager.IotMqttWsConnectionManager; @@ -521,7 +522,7 @@ public class IotMqttWsUpstreamHandler { } // 3. 获取设备信息 - IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(username); + IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username); if (deviceInfo == null) { log.warn("[authenticateDevice][用户名格式不正确,username: {}]", username); return null; diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/router/IotTcpUpstreamHandler.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/router/IotTcpUpstreamHandler.java index 0aff8f72f2..554a384cd7 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/tcp/router/IotTcpUpstreamHandler.java +++ b/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; import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpBinaryDeviceMessageCodec; import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpJsonDeviceMessageCodec; @@ -162,7 +163,7 @@ public class IotTcpUpstreamHandler implements Handler { } // 2.1 解析设备信息 - IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.getUsername()); + IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.getUsername()); if (deviceInfo == null) { sendErrorResponse(socket, message.getRequestId(), "解析设备信息失败", codecType); return; diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenService.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenService.java index 9aab67236b..6864c8de73 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenService.java +++ b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenService.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.gateway.service.auth; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; /** * IoT 设备 Token Service 接口 @@ -24,7 +24,7 @@ public interface IotDeviceTokenService { * @param token 设备 Token * @return 设备信息 */ - IotDeviceAuthUtils.DeviceInfo verifyToken(String token); + IotDeviceIdentity verifyToken(String token); /** * 解析用户名 @@ -32,6 +32,6 @@ public interface IotDeviceTokenService { * @param username 用户名 * @return 设备信息 */ - IotDeviceAuthUtils.DeviceInfo parseUsername(String username); + IotDeviceIdentity parseUsername(String username); } diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenServiceImpl.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenServiceImpl.java index 79ba4e77e7..cc6e3fd37b 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/auth/IotDeviceTokenServiceImpl.java +++ b/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; import cn.hutool.jwt.JWT; import cn.hutool.jwt.JWTUtil; import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.module.iot.core.topic.IotDeviceIdentity; import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; import jakarta.annotation.Resource; @@ -48,7 +49,7 @@ public class IotDeviceTokenServiceImpl implements IotDeviceTokenService { } @Override - public IotDeviceAuthUtils.DeviceInfo verifyToken(String token) { + public IotDeviceIdentity verifyToken(String token) { Assert.notBlank(token, "token 不能为空"); // 校验 JWT Token boolean verify = JWTUtil.verify(token, gatewayProperties.getToken().getSecret().getBytes()); @@ -68,11 +69,11 @@ public class IotDeviceTokenServiceImpl implements IotDeviceTokenService { String deviceName = payload.getStr("deviceName"); Assert.notBlank(productKey, "productKey 不能为空"); Assert.notBlank(deviceName, "deviceName 不能为空"); - return new IotDeviceAuthUtils.DeviceInfo().setProductKey(productKey).setDeviceName(deviceName); + return new IotDeviceIdentity(productKey, deviceName); } @Override - public IotDeviceAuthUtils.DeviceInfo parseUsername(String username) { + public IotDeviceIdentity parseUsername(String username) { return IotDeviceAuthUtils.parseUsername(username); } diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java b/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java index 271a103490..97312559b9 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java +++ b/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; 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.biz.dto.IotSubDeviceRegisterFullReqDTO; import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterRespDTO; +import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterRespDTO; import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties; + +import java.util.List; import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -61,6 +65,11 @@ public class IotDeviceApiImpl implements IotDeviceCommonApi { return doPost("/register", reqDTO, new ParameterizedTypeReference<>() { }); } + @Override + public CommonResult> registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO) { + return doPost("/register-sub", reqDTO, new ParameterizedTypeReference<>() { }); + } + private CommonResult doPost(String url, T body, ParameterizedTypeReference> responseType) { try { diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotDeviceRegisterHttpProtocolIntegrationTest.java b/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotDeviceRegisterHttpProtocolIntegrationTest.java deleted file mode 100644 index 2db36f549b..0000000000 --- a/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotDeviceRegisterHttpProtocolIntegrationTest.java +++ /dev/null @@ -1,203 +0,0 @@ -package cn.iocoder.yudao.module.iot.gateway.protocol.http; - -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.IdUtil; -import cn.hutool.http.HttpResponse; -import cn.hutool.http.HttpUtil; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; -import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; -import cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO; -import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; -import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.Test; - -import java.util.Collections; - -// TODO @AI:合并到 IotDirectDeviceHttpProtocolIntegrationTest 里呀,没必要拆开;只搞一个直连设备的注册就好了; -/** - * IoT 设备动态注册 HTTP 协议集成测试(手动测试) - * - *

测试场景:一型一密(One Type One Secret)动态注册机制 - * - *

前置条件: - *

    - *
  1. 产品已开启动态注册(registerEnabled = true)
  2. - *
  3. 设备已预先创建(预注册模式)
  4. - *
  5. 设备 deviceSecret 为空(未激活状态)
  6. - *
- * - *

使用步骤: - *

    - *
  1. 启动 yudao-module-iot-gateway 服务(HTTP 端口 8092)
  2. - *
  3. 运行 {@link #testDeviceRegister()} 测试直连设备/网关动态注册
  4. - *
  5. 运行 {@link #testSubDeviceRegister()} 测试子设备动态注册(需要先获取网关 Token)
  6. - *
- * - * @author 芋道源码 - * @see 阿里云 - 一型一密 - */ -@Slf4j -@SuppressWarnings("HttpUrlsUsage") -public class IotDeviceRegisterHttpProtocolIntegrationTest { - - private static final String SERVER_HOST = "127.0.0.1"; - private static final int SERVER_PORT = 8092; - - // ===================== 直连设备/网关动态注册配置(根据实际情况修改) ===================== - /** - * 产品 Key(需要开启动态注册) - */ - private static final String PRODUCT_KEY = "4aymZgOTOOCrDKRT"; - /** - * 产品密钥(从 iot_product 表的 product_secret 字段获取) - */ - private static final String PRODUCT_SECRET = "your_product_secret"; - /** - * 设备名称(需要预先创建,deviceSecret 为空) - */ - private static final String DEVICE_NAME = "test-register-device"; - - // ===================== 网关设备信息(用于子设备动态注册) ===================== - private static final String GATEWAY_PRODUCT_KEY = "m6XcS1ZJ3TW8eC0v"; - private static final String GATEWAY_DEVICE_NAME = "sub-ddd"; - private static final String GATEWAY_DEVICE_SECRET = "b3d62c70f8a4495487ed1d35d61ac2b3"; - - /** - * 网关设备 Token:从网关认证获取后,粘贴到这里 - */ - private static final String GATEWAY_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoibTZYY1MxWkozVFc4ZUMwdiIsImV4cCI6MTc2OTg2NjY3OCwiZGV2aWNlTmFtZSI6InN1Yi1kZGQifQ.nCLSAfHEjXLtTDRXARjOoFqpuo5WfArjFWweUAzrjKU"; - - // ===================== 子设备信息(用于子设备动态注册) ===================== - private static final String SUB_DEVICE_PRODUCT_KEY = "jAufEMTF1W6wnPhn"; - private static final String SUB_DEVICE_NAME = "test-sub-register-device"; - - // ===================== 直连设备/网关动态注册测试 ===================== - - /** - * 直连设备/网关动态注册测试 - *

- * 使用产品密钥(productSecret)进行签名验证,成功后返回设备密钥(deviceSecret) - *

- * 注意:此接口不需要 Token 认证 - */ - @Test - public void testDeviceRegister() { - // 1.1 构建请求 - String url = String.format("http://%s:%d/auth/register/device", SERVER_HOST, SERVER_PORT); - // 1.2 生成签名 - String random = IdUtil.fastSimpleUUID(); - String sign = IotDeviceAuthUtils.buildRegisterSign(PRODUCT_SECRET, PRODUCT_KEY, DEVICE_NAME, random); - // 1.3 构建请求参数 - IotDeviceRegisterReqDTO reqDTO = new IotDeviceRegisterReqDTO(); - reqDTO.setProductKey(PRODUCT_KEY); - reqDTO.setDeviceName(DEVICE_NAME); - reqDTO.setRandom(random); - reqDTO.setSign(sign); - String payload = JsonUtils.toJsonString(reqDTO); - // 1.4 输出请求 - log.info("[testDeviceRegister][请求 URL: {}]", url); - log.info("[testDeviceRegister][请求体: {}]", payload); - - // 2.1 发送请求 - String response = HttpUtil.post(url, payload); - // 2.2 输出结果 - log.info("[testDeviceRegister][响应体: {}]", response); - log.info("[testDeviceRegister][成功后可使用返回的 deviceSecret 进行一机一密认证]"); - } - - /** - * 测试动态注册后使用 deviceSecret 进行认证 - *

- * 此测试需要先执行 testDeviceRegister 获取 deviceSecret - */ - @Test - public void testAuthAfterRegister() { - // 1.1 构建请求 - String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT); - // TODO 将 testDeviceRegister 返回的 deviceSecret 填入此处 - String deviceSecret = "返回的deviceSecret"; - IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, deviceSecret); - IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO() - .setClientId(authInfo.getClientId()) - .setUsername(authInfo.getUsername()) - .setPassword(authInfo.getPassword()); - String payload = JsonUtils.toJsonString(authReqDTO); - // 1.2 输出请求 - log.info("[testAuthAfterRegister][请求 URL: {}]", url); - log.info("[testAuthAfterRegister][请求体: {}]", payload); - - // 2.1 发送请求 - String response = HttpUtil.post(url, payload); - // 2.2 输出结果 - log.info("[testAuthAfterRegister][响应体: {}]", response); - } - - // ===================== 网关认证测试 ===================== - - /** - * 网关设备认证测试:获取网关设备 Token(用于后续子设备动态注册) - */ - @Test - public void testGatewayAuth() { - // 1.1 构建请求 - String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT); - IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo( - GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET); - IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO() - .setClientId(authInfo.getClientId()) - .setUsername(authInfo.getUsername()) - .setPassword(authInfo.getPassword()); - String payload = JsonUtils.toJsonString(authReqDTO); - // 1.2 输出请求 - log.info("[testGatewayAuth][请求 URL: {}]", url); - log.info("[testGatewayAuth][请求体: {}]", payload); - - // 2.1 发送请求 - String response = HttpUtil.post(url, payload); - // 2.2 输出结果 - log.info("[testGatewayAuth][响应体: {}]", response); - log.info("[testGatewayAuth][请将返回的 token 复制到 GATEWAY_TOKEN 常量中]"); - } - - // ===================== 子设备动态注册测试 ===================== - - /** - * 子设备动态注册测试 - *

- * 网关设备代理子设备进行动态注册,平台返回子设备的 deviceSecret - *

- * 注意:此接口需要网关 Token 认证 - */ - @Test - public void testSubDeviceRegister() { - // 1.1 构建请求(需要网关认证) - String url = String.format("http://%s:%d/auth/register/sub-device/%s/%s", - SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME); - // 1.2 构建请求参数 - IotSubDeviceRegisterReqDTO subDevice = new IotSubDeviceRegisterReqDTO(); - subDevice.setProductKey(SUB_DEVICE_PRODUCT_KEY); - subDevice.setDeviceName(SUB_DEVICE_NAME); - String payload = JsonUtils.toJsonString(MapUtil.builder() - .put("id", IdUtil.fastSimpleUUID()) - .put("method", IotDeviceMessageMethodEnum.SUB_DEVICE_REGISTER.getMethod()) - .put("version", "1.0") - .put("params", Collections.singletonList(subDevice)) - .build()); - // 1.3 输出请求 - log.info("[testSubDeviceRegister][请求 URL: {}]", url); - log.info("[testSubDeviceRegister][请求体: {}]", payload); - - // 2.1 发送请求(需要网关 Token) - try (HttpResponse httpResponse = HttpUtil.createPost(url) - .header("Authorization", GATEWAY_TOKEN) - .body(payload) - .execute()) { - // 2.2 输出结果 - log.info("[testSubDeviceRegister][响应体: {}]", httpResponse.body()); - log.info("[testSubDeviceRegister][成功后可使用返回的 deviceSecret 进行子设备认证]"); - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotDirectDeviceHttpProtocolIntegrationTest.java b/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotDirectDeviceHttpProtocolIntegrationTest.java index 70e4114447..7f6bfc1c1b 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotDirectDeviceHttpProtocolIntegrationTest.java +++ b/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; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceAuthReqDTO; import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; +import cn.iocoder.yudao.module.iot.core.topic.auth.IotDeviceRegisterReqDTO; import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO; import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO; import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; @@ -22,6 +23,7 @@ import org.junit.jupiter.api.Test; *

使用步骤: *

    *
  1. 启动 yudao-module-iot-gateway 服务(HTTP 端口 8092)
  2. + *
  3. 运行 {@link #testDeviceRegister()} 测试直连设备动态注册(一型一密)
  4. *
  5. 运行 {@link #testAuth()} 获取设备 token,将返回的 token 粘贴到 {@link #TOKEN} 常量
  6. *
  7. 运行以下测试方法: *
      @@ -45,11 +47,78 @@ public class IotDirectDeviceHttpProtocolIntegrationTest { private static final String DEVICE_NAME = "small"; private static final String DEVICE_SECRET = "0baa4c2ecc104ae1a26b4070c218bdf3"; + /** + * 产品密钥(从 iot_product 表的 product_secret 字段获取),用于动态注册 + */ + private static final String PRODUCT_SECRET = "your_product_secret"; + + /** + * 动态注册的设备名称 + */ + private static final String REGISTER_DEVICE_NAME = "test-register-device"; + /** * 直连设备 Token:从 {@link #testAuth()} 方法获取后,粘贴到这里 */ private static final String TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9kdWN0S2V5IjoiNGF5bVpnT1RPT0NyREtSVCIsImV4cCI6MTc2OTMwNTA1NSwiZGV2aWNlTmFtZSI6InNtYWxsIn0.mf3MEATCn5bp6cXgULunZjs8d00RGUxj96JEz0hMS7k"; + // ===================== 动态注册测试 ===================== + + /** + * 直连设备动态注册测试(一型一密) + *

      + * 使用产品密钥(productSecret)验证身份,成功后返回设备密钥(deviceSecret) + *

      + * 注意:此接口不需要 Token 认证 + */ + @Test + public void testDeviceRegister() { + // 1.1 构建请求 + String url = String.format("http://%s:%d/auth/register/device", SERVER_HOST, SERVER_PORT); + // 1.2 构建请求参数 + IotDeviceRegisterReqDTO reqDTO = new IotDeviceRegisterReqDTO(); + reqDTO.setProductKey(PRODUCT_KEY); + reqDTO.setDeviceName(REGISTER_DEVICE_NAME); + reqDTO.setProductSecret(PRODUCT_SECRET); + String payload = JsonUtils.toJsonString(reqDTO); + // 1.3 输出请求 + log.info("[testDeviceRegister][请求 URL: {}]", url); + log.info("[testDeviceRegister][请求体: {}]", payload); + + // 2.1 发送请求 + String response = HttpUtil.post(url, payload); + // 2.2 输出结果 + log.info("[testDeviceRegister][响应体: {}]", response); + log.info("[testDeviceRegister][成功后可使用返回的 deviceSecret 进行一机一密认证]"); + } + + /** + * 测试动态注册后使用 deviceSecret 进行认证 + *

      + * 此测试需要先执行 testDeviceRegister 获取 deviceSecret + */ + @Test + public void testAuthAfterRegister() { + // 1.1 构建请求 + String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT); + // TODO 将 testDeviceRegister 返回的 deviceSecret 填入此处 + String deviceSecret = "返回的deviceSecret"; + IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, REGISTER_DEVICE_NAME, deviceSecret); + IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO() + .setClientId(authInfo.getClientId()) + .setUsername(authInfo.getUsername()) + .setPassword(authInfo.getPassword()); + String payload = JsonUtils.toJsonString(authReqDTO); + // 1.2 输出请求 + log.info("[testAuthAfterRegister][请求 URL: {}]", url); + log.info("[testAuthAfterRegister][请求体: {}]", payload); + + // 2.1 发送请求 + String response = HttpUtil.post(url, payload); + // 2.2 输出结果 + log.info("[testAuthAfterRegister][响应体: {}]", response); + } + // ===================== 认证测试 ===================== /** diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotGatewayDeviceHttpProtocolIntegrationTest.java b/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotGatewayDeviceHttpProtocolIntegrationTest.java index 281ff4f012..9c4a64361e 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotGatewayDeviceHttpProtocolIntegrationTest.java +++ b/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 { public void testAuth() { // 1.1 构建请求 String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT); - IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo( + IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo( GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME, GATEWAY_DEVICE_SECRET); IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO() .setClientId(authInfo.getClientId()) @@ -108,7 +108,7 @@ public class IotGatewayDeviceHttpProtocolIntegrationTest { String url = String.format("http://%s:%d/topic/sys/%s/%s/thing/topo/add", SERVER_HOST, SERVER_PORT, GATEWAY_PRODUCT_KEY, GATEWAY_DEVICE_NAME); // 1.2 构建子设备认证信息 - IotDeviceAuthUtils.AuthInfo subAuthInfo = IotDeviceAuthUtils.getAuthInfo( + IotDeviceAuthReqDTO subAuthInfo = IotDeviceAuthUtils.getAuthInfo( SUB_DEVICE_PRODUCT_KEY, SUB_DEVICE_NAME, SUB_DEVICE_SECRET); IotDeviceAuthReqDTO subDeviceAuth = new IotDeviceAuthReqDTO() .setClientId(subAuthInfo.getClientId()) diff --git a/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotGatewaySubDeviceHttpProtocolIntegrationTest.java b/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotGatewaySubDeviceHttpProtocolIntegrationTest.java index 1428d8e527..7bb83a52b9 100644 --- a/yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/IotGatewaySubDeviceHttpProtocolIntegrationTest.java +++ b/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 { public void testAuth() { // 1.1 构建请求 String url = String.format("http://%s:%d/auth", SERVER_HOST, SERVER_PORT); - IotDeviceAuthUtils.AuthInfo authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET); + IotDeviceAuthReqDTO authInfo = IotDeviceAuthUtils.getAuthInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET); IotDeviceAuthReqDTO authReqDTO = new IotDeviceAuthReqDTO() .setClientId(authInfo.getClientId()) .setUsername(authInfo.getUsername())