|
|
@@ -9,7 +9,11 @@ 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.IotDeviceAuthReqDTO;
|
|
|
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
|
|
|
+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.core.topic.IotDeviceIdentity;
|
|
|
+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.util.IotDeviceAuthUtils;
|
|
|
import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpBinaryDeviceMessageCodec;
|
|
|
import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpJsonDeviceMessageCodec;
|
|
|
@@ -129,6 +133,9 @@ public class IotUdpUpstreamHandler {
|
|
|
if (AUTH_METHOD.equals(message.getMethod())) {
|
|
|
// 认证请求
|
|
|
handleAuthenticationRequest(message, codecType, senderAddress, socket);
|
|
|
+ } else if (IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod().equals(message.getMethod())) {
|
|
|
+ // 设备动态注册请求
|
|
|
+ handleRegisterRequest(message, codecType, senderAddress, socket);
|
|
|
} else {
|
|
|
// 业务消息
|
|
|
handleBusinessRequest(message, codecType, senderAddress, socket);
|
|
|
@@ -168,7 +175,7 @@ public class IotUdpUpstreamHandler {
|
|
|
}
|
|
|
|
|
|
// 2.1 解析设备信息
|
|
|
- IotDeviceAuthUtils.DeviceInfo deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.getUsername());
|
|
|
+ IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(authParams.getUsername());
|
|
|
if (deviceInfo == null) {
|
|
|
sendErrorResponse(socket, senderAddress, message.getRequestId(), "解析设备信息失败", codecType);
|
|
|
return;
|
|
|
@@ -200,6 +207,45 @@ public class IotUdpUpstreamHandler {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 处理设备动态注册请求(一型一密,不需要 Token)
|
|
|
+ *
|
|
|
+ * @param message 消息信息
|
|
|
+ * @param codecType 消息编解码类型
|
|
|
+ * @param senderAddress 发送者地址
|
|
|
+ * @param socket UDP Socket
|
|
|
+ * @see <a href="https://help.aliyun.com/zh/iot/user-guide/unique-certificate-per-product-verification">阿里云 - 一型一密</a>
|
|
|
+ */
|
|
|
+ private void handleRegisterRequest(IotDeviceMessage message, String codecType,
|
|
|
+ InetSocketAddress senderAddress, DatagramSocket socket) {
|
|
|
+ String addressKey = sessionManager.buildAddressKey(senderAddress);
|
|
|
+ try {
|
|
|
+ // 1. 解析注册参数
|
|
|
+ IotDeviceRegisterReqDTO registerParams = parseRegisterParams(message.getParams());
|
|
|
+ if (registerParams == null) {
|
|
|
+ log.warn("[handleRegisterRequest][注册参数解析失败,来源: {}]", addressKey);
|
|
|
+ sendErrorResponse(socket, senderAddress, message.getRequestId(), "注册参数不完整", codecType);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 调用动态注册
|
|
|
+ CommonResult<IotDeviceRegisterRespDTO> result = deviceApi.registerDevice(registerParams);
|
|
|
+ if (result.isError()) {
|
|
|
+ log.warn("[handleRegisterRequest][注册失败,来源: {},错误: {}]", addressKey, result.getMsg());
|
|
|
+ sendErrorResponse(socket, senderAddress, message.getRequestId(), result.getMsg(), codecType);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 发送成功响应(包含 deviceSecret)
|
|
|
+ sendRegisterSuccessResponse(socket, senderAddress, message.getRequestId(), result.getData(), codecType);
|
|
|
+ log.info("[handleRegisterRequest][注册成功,设备名: {},来源: {}]",
|
|
|
+ registerParams.getDeviceName(), addressKey);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[handleRegisterRequest][注册处理异常,来源: {}]", addressKey, e);
|
|
|
+ sendErrorResponse(socket, senderAddress, message.getRequestId(), "注册处理异常", codecType);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 处理业务请求
|
|
|
*
|
|
|
@@ -225,7 +271,7 @@ public class IotUdpUpstreamHandler {
|
|
|
return;
|
|
|
}
|
|
|
// 1.2 验证 token,获取设备信息
|
|
|
- IotDeviceAuthUtils.DeviceInfo deviceInfo = deviceTokenService.verifyToken(token);
|
|
|
+ IotDeviceIdentity deviceInfo = deviceTokenService.verifyToken(token);
|
|
|
if (deviceInfo == null) {
|
|
|
log.warn("[handleBusinessRequest][token 无效或已过期,来源: {}]", addressKey);
|
|
|
sendErrorResponse(socket, senderAddress, message.getRequestId(), "token 无效或已过期", codecType);
|
|
|
@@ -317,14 +363,15 @@ public class IotUdpUpstreamHandler {
|
|
|
private void sendAuthSuccessResponse(DatagramSocket socket, InetSocketAddress address,
|
|
|
String requestId, String token, String codecType) {
|
|
|
try {
|
|
|
+ // 构建响应数据
|
|
|
Object responseData = MapUtil.builder()
|
|
|
.put("success", true)
|
|
|
.put("token", token)
|
|
|
.put("message", "认证成功")
|
|
|
.build();
|
|
|
-
|
|
|
IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, AUTH_METHOD, responseData, 0, "认证成功");
|
|
|
byte[] encodedData = deviceMessageService.encodeDeviceMessage(responseMessage, codecType);
|
|
|
+ // 发送响应
|
|
|
socket.send(Buffer.buffer(encodedData), address.getPort(), address.getHostString(), result -> {
|
|
|
if (result.failed()) {
|
|
|
log.error("[sendAuthSuccessResponse][发送认证成功响应失败,地址: {}]",
|
|
|
@@ -337,6 +384,41 @@ public class IotUdpUpstreamHandler {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 发送注册成功响应(包含 deviceSecret)
|
|
|
+ *
|
|
|
+ * @param socket UDP Socket
|
|
|
+ * @param address 目标地址
|
|
|
+ * @param requestId 请求 ID
|
|
|
+ * @param registerResp 注册响应
|
|
|
+ * @param codecType 消息编解码类型
|
|
|
+ */
|
|
|
+ private void sendRegisterSuccessResponse(DatagramSocket socket, InetSocketAddress address,
|
|
|
+ String requestId, IotDeviceRegisterRespDTO registerResp,
|
|
|
+ String codecType) {
|
|
|
+ try {
|
|
|
+ // 构建响应数据
|
|
|
+ Object responseData = MapUtil.builder()
|
|
|
+ .put("success", true)
|
|
|
+ .put("deviceSecret", registerResp.getDeviceSecret())
|
|
|
+ .put("message", "注册成功")
|
|
|
+ .build();
|
|
|
+ IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId,
|
|
|
+ IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod(), responseData, 0, "注册成功");
|
|
|
+ // 发送响应
|
|
|
+ byte[] encodedData = deviceMessageService.encodeDeviceMessage(responseMessage, codecType);
|
|
|
+ socket.send(Buffer.buffer(encodedData), address.getPort(), address.getHostString(), result -> {
|
|
|
+ if (result.failed()) {
|
|
|
+ log.error("[sendRegisterSuccessResponse][发送注册成功响应失败,地址: {}]",
|
|
|
+ sessionManager.buildAddressKey(address), result.cause());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[sendRegisterSuccessResponse][发送注册成功响应异常,地址: {}]",
|
|
|
+ sessionManager.buildAddressKey(address), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 发送成功响应
|
|
|
*
|
|
|
@@ -437,4 +519,46 @@ public class IotUdpUpstreamHandler {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 解析注册参数
|
|
|
+ *
|
|
|
+ * @param params 参数对象(通常为 Map 类型)
|
|
|
+ * @return 注册参数 DTO,解析失败时返回 null
|
|
|
+ */
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ private IotDeviceRegisterReqDTO parseRegisterParams(Object params) {
|
|
|
+ if (params == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 参数默认为 Map 类型,直接转换
|
|
|
+ if (params instanceof Map) {
|
|
|
+ Map<String, Object> paramMap = (Map<String, Object>) params;
|
|
|
+ String productKey = MapUtil.getStr(paramMap, "productKey");
|
|
|
+ String deviceName = MapUtil.getStr(paramMap, "deviceName");
|
|
|
+ String productSecret = MapUtil.getStr(paramMap, "productSecret");
|
|
|
+ if (StrUtil.hasBlank(productKey, deviceName, productSecret)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return new IotDeviceRegisterReqDTO()
|
|
|
+ .setProductKey(productKey)
|
|
|
+ .setDeviceName(deviceName)
|
|
|
+ .setProductSecret(productSecret);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果已经是目标类型,直接返回
|
|
|
+ if (params instanceof IotDeviceRegisterReqDTO) {
|
|
|
+ return (IotDeviceRegisterReqDTO) params;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 其他情况尝试 JSON 转换
|
|
|
+ String jsonStr = JsonUtils.toJsonString(params);
|
|
|
+ return JsonUtils.parseObject(jsonStr, IotDeviceRegisterReqDTO.class);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("[parseRegisterParams][解析注册参数({})失败]", params, e);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|