Commit b4ce72ea7d4caec62b29ed4dcbf6f366a6266009
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<RoutingContext> | @@ -74,7 +74,7 @@ public abstract class IotHttpAbstractHandler implements Handler<RoutingContext> | ||
| 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<NetSocket> { | @@ -162,7 +163,7 @@ public class IotTcpUpstreamHandler implements Handler<NetSocket> { | ||
| 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()) |