mirror of
https://gitee.com/zhijiantianya/ruoyi-vue-pro.git
synced 2026-03-22 05:07:17 +08:00
feat(iot):【网关设备:72%】动态注册的初步实现(未测试、额外优化代码),基于 stateful-sauteeing-pillow.md 规划
This commit is contained in:
@@ -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<List<IotSubDeviceRegisterRespDTO>> registerSubDevices(@RequestBody IotSubDeviceRegisterFullReqDTO reqDTO) {
|
||||
return success(deviceService.registerSubDevices(reqDTO));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public class IotProductSaveReqVO {
|
||||
private String codecType;
|
||||
|
||||
@Schema(description = "是否开启动态注册", example = "false")
|
||||
@NotEmpty(message = "是否开启动态注册不能为空")
|
||||
@NotNull(message = "是否开启动态注册不能为空")
|
||||
private Boolean registerEnabled;
|
||||
|
||||
}
|
||||
@@ -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, "产品分类不存在");
|
||||
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
* 网关子设备动态注册
|
||||
* <p>
|
||||
* 与 {@link #handleSubDeviceRegisterMessage} 方法的区别:
|
||||
* 该方法网关设备信息通过 reqDTO 参数传入,而 {@link #handleSubDeviceRegisterMessage} 方法通过 gatewayDevice 参数传入
|
||||
*
|
||||
* @param reqDTO 子设备注册请求(包含网关设备信息)
|
||||
* @return 注册结果列表
|
||||
*/
|
||||
List<IotSubDeviceRegisterRespDTO> registerSubDevices(@Valid IotSubDeviceRegisterFullReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 处理子设备动态注册消息(网关设备上报)
|
||||
*
|
||||
@@ -367,12 +375,4 @@ public interface IotDeviceService {
|
||||
*/
|
||||
List<IotSubDeviceRegisterRespDTO> handleSubDeviceRegisterMessage(IotDeviceMessage message, IotDeviceDO gatewayDevice);
|
||||
|
||||
/**
|
||||
* 设备动态注册(直连设备/网关)
|
||||
*
|
||||
* @param reqDTO 动态注册请求
|
||||
* @return 注册结果(包含 DeviceSecret)
|
||||
*/
|
||||
IotDeviceRegisterRespDTO registerDevice(@Valid IotDeviceRegisterReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
||||
@@ -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<IotSubDeviceRegisterRespDTO> registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO) {
|
||||
// 1. 校验网关设备
|
||||
IotDeviceDO gatewayDevice = getSelf().getDeviceFromCache(reqDTO.getGatewayProductKey(), reqDTO.getGatewayDeviceName());
|
||||
|
||||
// 2. 遍历注册每个子设备
|
||||
return registerSubDevices0(gatewayDevice, reqDTO.getSubDevices());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IotSubDeviceRegisterRespDTO> 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<IotSubDeviceRegisterReqDTO> paramsList = JsonUtils.convertList(message.getParams(),
|
||||
IotSubDeviceRegisterReqDTO.class);
|
||||
if (CollUtil.isEmpty(paramsList)) {
|
||||
List<IotSubDeviceRegisterReqDTO> subDevices = JsonUtils.convertList(message.getParams(), IotSubDeviceRegisterReqDTO.class);
|
||||
|
||||
// 2. 遍历注册每个子设备
|
||||
return registerSubDevices0(gatewayDevice, subDevices);
|
||||
}
|
||||
|
||||
private List<IotSubDeviceRegisterRespDTO> registerSubDevices0(IotDeviceDO gatewayDevice,
|
||||
List<IotSubDeviceRegisterReqDTO> 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<IotSubDeviceRegisterRespDTO> results = new ArrayList<>(paramsList.size());
|
||||
for (IotSubDeviceRegisterReqDTO params : paramsList) {
|
||||
List<IotSubDeviceRegisterRespDTO> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<IotDeviceRespDTO> getDevice(IotDeviceGetReqDTO infoReqDTO);
|
||||
|
||||
/**
|
||||
* 设备动态注册(一型一密)
|
||||
* 直连/网关设备动态注册(一型一密)
|
||||
*
|
||||
* @param reqDTO 动态注册请求
|
||||
* @return 注册结果(包含 DeviceSecret)
|
||||
*/
|
||||
CommonResult<IotDeviceRegisterRespDTO> registerDevice(IotDeviceRegisterReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 网关子设备动态注册(网关代理转发)
|
||||
*
|
||||
* @param reqDTO 子设备注册请求(包含网关标识和子设备列表)
|
||||
* @return 注册结果列表
|
||||
*/
|
||||
CommonResult<List<IotSubDeviceRegisterRespDTO>> registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO);
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
* <p>
|
||||
* 额外包含了网关设备的标识信息
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class IotSubDeviceRegisterFullReqDTO {
|
||||
|
||||
/**
|
||||
* 网关设备 ProductKey
|
||||
*/
|
||||
@NotEmpty(message = "网关产品标识不能为空")
|
||||
private String gatewayProductKey;
|
||||
|
||||
/**
|
||||
* 网关设备 DeviceName
|
||||
*/
|
||||
@NotEmpty(message = "网关设备名称不能为空")
|
||||
private String gatewayDeviceName;
|
||||
|
||||
/**
|
||||
* 子设备注册列表
|
||||
*/
|
||||
@NotNull(message = "子设备注册列表不能为空")
|
||||
private List<IotSubDeviceRegisterReqDTO> subDevices;
|
||||
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import lombok.Data;
|
||||
/**
|
||||
* IoT 设备动态注册 Request DTO
|
||||
* <p>
|
||||
* 用于直连设备/网关的一型一密动态注册:使用 ProductSecret 验证签名,返回 DeviceSecret
|
||||
* 用于直连设备/网关的一型一密动态注册:使用 productSecret 验证,返回 deviceSecret
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a>
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import lombok.Data;
|
||||
* 用于 thing.event.post 消息的 params 参数
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services">阿里云 - 设备上报事件</a>
|
||||
* @see <a href="http://help.aliyun.com/zh/marketplace/device-reporting-events">阿里云 - 设备上报事件</a>
|
||||
*/
|
||||
@Data
|
||||
public class IotDeviceEventPostReqDTO {
|
||||
|
||||
@@ -11,7 +11,7 @@ import java.util.Map;
|
||||
* 本质是一个 Map,key 为属性标识符,value 为属性值
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services">阿里云 - 设备上报属性</a>
|
||||
* @see <a href="http://help.aliyun.com/zh/marketplace/device-reporting-attributes">阿里云 - 设备上报属性</a>
|
||||
*/
|
||||
public class IotDevicePropertyPostReqDTO extends HashMap<String, Object> {
|
||||
|
||||
|
||||
@@ -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;
|
||||
/**
|
||||
* 计算动态注册签名
|
||||
* <p>
|
||||
* 参考阿里云规范,参数按字典序排列拼接
|
||||
*
|
||||
* @param productSecret 产品密钥
|
||||
* @param productKey 产品标识
|
||||
* @param deviceName 设备名称
|
||||
* @param random 随机数
|
||||
* @return 签名
|
||||
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">一型一密</a>
|
||||
*/
|
||||
public static String buildRegisterSign(String productSecret, String productKey, String deviceName, String random) {
|
||||
String content = "deviceName" + deviceName + "productKey" + productKey + "random" + random;
|
||||
return DigestUtil.hmac(HmacAlgorithm.HmacSHA256, StrUtil.utf8Bytes(productSecret))
|
||||
.digestHex(content);
|
||||
}
|
||||
|
||||
// TODO @AI:是不是调用方自己验证就好了,不要这里面抽;
|
||||
/**
|
||||
* 验证动态注册签名
|
||||
*
|
||||
* @param productSecret 产品密钥
|
||||
* @param productKey 产品标识
|
||||
* @param deviceName 设备名称
|
||||
* @param random 随机数
|
||||
* @param sign 待验证的签名
|
||||
* @return 是否验证通过
|
||||
*/
|
||||
public static boolean verifyRegisterSign(String productSecret, String productKey,
|
||||
String deviceName, String random, String sign) {
|
||||
String expectedSign = buildRegisterSign(productSecret, productKey, deviceName, random);
|
||||
return expectedSign.equals(sign);
|
||||
return new IotDeviceIdentity(usernameParts[1], usernameParts[0]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<RoutingContext>
|
||||
}
|
||||
|
||||
// 校验 token
|
||||
IotDeviceAuthUtils.DeviceInfo deviceInfo = deviceTokenService.verifyToken(token);
|
||||
IotDeviceIdentity deviceInfo = deviceTokenService.verifyToken(token);
|
||||
Assert.notNull(deviceInfo, "设备信息不能为空");
|
||||
// 校验设备信息是否匹配
|
||||
if (ObjUtil.notEqual(productKey, deviceInfo.getProductKey())
|
||||
|
||||
@@ -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 不能为空位");
|
||||
|
||||
@@ -33,7 +33,6 @@ public class IotHttpRegisterHandler extends IotHttpAbstractHandler {
|
||||
@Override
|
||||
public CommonResult<Object> 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<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(reqDTO);
|
||||
result.checkError();
|
||||
|
||||
|
||||
@@ -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<cn.iocoder.yudao.module.iot.core.topic.auth.IotSubDeviceRegisterReqDTO> 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<List<IotSubDeviceRegisterRespDTO>> result = deviceApi.registerSubDevices(reqDTO);
|
||||
result.checkError();
|
||||
|
||||
// 3. 返回结果
|
||||
return success(responseData);
|
||||
// 4. 返回结果
|
||||
return success(result.getData());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<NetSocket> {
|
||||
}
|
||||
|
||||
// 2.1 解析设备信息
|
||||
IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.getUsername());
|
||||
IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.getUsername());
|
||||
if (deviceInfo == null) {
|
||||
sendErrorResponse(socket, message.getRequestId(), "解析设备信息失败", codecType);
|
||||
return;
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<List<IotSubDeviceRegisterRespDTO>> registerSubDevices(IotSubDeviceRegisterFullReqDTO reqDTO) {
|
||||
return doPost("/register-sub", reqDTO, new ParameterizedTypeReference<>() { });
|
||||
}
|
||||
|
||||
private <T, R> CommonResult<R> doPost(String url, T body,
|
||||
ParameterizedTypeReference<CommonResult<R>> responseType) {
|
||||
try {
|
||||
|
||||
@@ -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 协议集成测试(手动测试)
|
||||
*
|
||||
* <p>测试场景:一型一密(One Type One Secret)动态注册机制
|
||||
*
|
||||
* <p><b>前置条件:</b>
|
||||
* <ol>
|
||||
* <li>产品已开启动态注册(registerEnabled = true)</li>
|
||||
* <li>设备已预先创建(预注册模式)</li>
|
||||
* <li>设备 deviceSecret 为空(未激活状态)</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>使用步骤:
|
||||
* <ol>
|
||||
* <li>启动 yudao-module-iot-gateway 服务(HTTP 端口 8092)</li>
|
||||
* <li>运行 {@link #testDeviceRegister()} 测试直连设备/网关动态注册</li>
|
||||
* <li>运行 {@link #testSubDeviceRegister()} 测试子设备动态注册(需要先获取网关 Token)</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a>
|
||||
*/
|
||||
@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";
|
||||
|
||||
// ===================== 直连设备/网关动态注册测试 =====================
|
||||
|
||||
/**
|
||||
* 直连设备/网关动态注册测试
|
||||
* <p>
|
||||
* 使用产品密钥(productSecret)进行签名验证,成功后返回设备密钥(deviceSecret)
|
||||
* <p>
|
||||
* 注意:此接口不需要 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 进行认证
|
||||
* <p>
|
||||
* 此测试需要先执行 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 常量中]");
|
||||
}
|
||||
|
||||
// ===================== 子设备动态注册测试 =====================
|
||||
|
||||
/**
|
||||
* 子设备动态注册测试
|
||||
* <p>
|
||||
* 网关设备代理子设备进行动态注册,平台返回子设备的 deviceSecret
|
||||
* <p>
|
||||
* 注意:此接口需要网关 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 进行子设备认证]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
* <p>使用步骤:
|
||||
* <ol>
|
||||
* <li>启动 yudao-module-iot-gateway 服务(HTTP 端口 8092)</li>
|
||||
* <li>运行 {@link #testDeviceRegister()} 测试直连设备动态注册(一型一密)</li>
|
||||
* <li>运行 {@link #testAuth()} 获取设备 token,将返回的 token 粘贴到 {@link #TOKEN} 常量</li>
|
||||
* <li>运行以下测试方法:
|
||||
* <ul>
|
||||
@@ -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";
|
||||
|
||||
// ===================== 动态注册测试 =====================
|
||||
|
||||
/**
|
||||
* 直连设备动态注册测试(一型一密)
|
||||
* <p>
|
||||
* 使用产品密钥(productSecret)验证身份,成功后返回设备密钥(deviceSecret)
|
||||
* <p>
|
||||
* 注意:此接口不需要 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 进行认证
|
||||
* <p>
|
||||
* 此测试需要先执行 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);
|
||||
}
|
||||
|
||||
// ===================== 认证测试 =====================
|
||||
|
||||
/**
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user