This commit is contained in:
FAN-yeB
2025-09-08 11:04:42 +08:00
parent d04ec9d510
commit eed9503391
11 changed files with 290 additions and 28 deletions
@@ -91,6 +91,12 @@ public interface Constant {
*/
String SERVER_WEBSOCKET = "server.websocket";
/**
* mqtt gateway 配置
*/
String SERVER_MQTT_GATEWAY = "server.mqtt_gateway";
/**
* ota地址
*/
@@ -77,6 +77,10 @@ public class OTAController {
@GetMapping
@Hidden
public ResponseEntity<String> getOTA() {
String mqttUdpConfig = sysParamsService.getValue(Constant.SERVER_MQTT_GATEWAY, false);
if(StringUtils.isBlank(mqttUdpConfig)) {
return ResponseEntity.ok("OTA接口不正常,缺少mqtt_gateway地址,请登录智控台,在参数管理找到【server.mqtt_gateway】配置");
}
String wsUrl = sysParamsService.getValue(Constant.SERVER_WEBSOCKET, true);
if (StringUtils.isBlank(wsUrl) || wsUrl.equals("null")) {
return ResponseEntity.ok("OTA接口不正常,缺少websocket地址,请登录智控台,在参数管理找到【server.websocket】配置");
@@ -23,6 +23,9 @@ public class DeviceReportRespDTO {
@Schema(description = "WebSocket配置")
private Websocket websocket;
@Schema(description = "MQTT Gateway配置")
private MQTT mqtt;
@Getter
@Setter
public static class Firmware {
@@ -70,4 +73,21 @@ public class DeviceReportRespDTO {
@Schema(description = "WebSocket服务器地址")
private String url;
}
}
@Getter
@Setter
public static class MQTT {
@Schema(description = "MQTT 配置网址")
private String endpoint;
@Schema(description = "MQTT 客户端唯一标识符")
private String client_id;
@Schema(description = "MQTT 认证用户名")
private String username;
@Schema(description = "MQTT 认证密码")
private String password;
@Schema(description = "ESP32 发布消息的主题")
private String publish_topic;
@Schema(description = "ESP32 订阅的主题")
private String subscribe_topic;
}
}
@@ -1,12 +1,16 @@
package xiaozhi.modules.device.service.impl;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext;
@@ -175,7 +179,22 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
}
response.setWebsocket(websocket);
// 添加MQTT UDP配置
// 从系统参数获取MQTT Gateway地址,如果未配置不使用默认值
String mqttUdpConfig = sysParamsService.getValue(Constant.SERVER_MQTT_GATEWAY, false);
if(!StringUtils.isBlank(mqttUdpConfig) && deviceById != null) {
try {
DeviceReportRespDTO.MQTT mqtt = buildMqttConfig(macAddress, clientId, deviceById);
if (mqtt != null) {
mqtt.setEndpoint(mqttUdpConfig);
response.setMqtt(mqtt);
}
} catch (Exception e) {
log.error("生成MQTT配置失败: {}", e.getMessage());
}
}
if (deviceById != null) {
// 如果设备存在,则异步更新上次连接时间和版本信息
String appVersion = deviceReport.getApplication() != null ? deviceReport.getApplication().getVersion()
@@ -437,4 +456,75 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
entity.setAutoUpdate(1);
baseDao.insert(entity);
}
/**
* 生成MQTT密码签名
* @param content 签名内容 (clientId + '|' + username)
* @param secretKey 密钥
* @return Base64编码的HMAC-SHA256签名
*/
private String generatePasswordSignature(String content, String secretKey) throws Exception {
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
hmac.init(keySpec);
byte[] signature = hmac.doFinal(content.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(signature);
}
/**
* 构建MQTT配置信息
* @param macAddress MAC地址
* @param clientId 客户端ID (UUID)
* @param device 设备信息
* @return MQTT配置对象
*/
private DeviceReportRespDTO.MQTT buildMqttConfig(String macAddress, String clientId, DeviceEntity device) throws Exception {
// 从环境变量或系统参数获取签名密钥
String signatureKey = System.getenv("MQTT_SIGNATURE_KEY");
if (StringUtils.isBlank(signatureKey)) {
// 如果环境变量没有,尝试从系统参数获取
signatureKey = sysParamsService.getValue("mqtt.signature_key", false);
}
if (StringUtils.isBlank(signatureKey)) {
log.warn("缺少MQTT_SIGNATURE_KEY,跳过MQTT配置生成");
return null;
}
// 构建客户端ID格式:groupId@@@macAddress_without_colon@@@uuid
String groupId = device.getBoard() != null ? device.getBoard() : "GID_default";
String deviceIdNoColon = macAddress.replace(":", "_");
String mqttClientId = String.format("%s@@@%s@@@%s", groupId, deviceIdNoColon, clientId);
// 构建用户数据(包含IP等信息)
Map<String, String> userData = new HashMap<>();
// 尝试获取客户端IP
try {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
String clientIp = request.getRemoteAddr();
userData.put("ip", clientIp);
}
} catch (Exception e) {
userData.put("ip", "unknown");
}
// 将用户数据编码为Base64 JSON
String userDataJson = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(userData);
String username = Base64.getEncoder().encodeToString(userDataJson.getBytes(StandardCharsets.UTF_8));
// 生成密码签名
String password = generatePasswordSignature(mqttClientId + "|" + username, signatureKey);
// 构建MQTT配置
DeviceReportRespDTO.MQTT mqtt = new DeviceReportRespDTO.MQTT();
mqtt.setClient_id(mqttClientId);
mqtt.setUsername(username);
mqtt.setPassword(password);
mqtt.setPublish_topic("device-server");
mqtt.setSubscribe_topic("devices/p2p/" + deviceIdNoColon);
return mqtt;
}
}
@@ -0,0 +1,7 @@
delete from `sys_params` where id = 108;
delete from `sys_params` where param_code = 'server.mqtt_gateway';
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (108, 'server.mqtt_gateway', 'null', 'string', 1, 'mqtt gateway 配置');
delete from `sys_params` where param_code = 'server.udp_gateway';
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (109, 'server.udp_gateway', 'null', 'string', 1, 'udp gateway 配置');
@@ -0,0 +1,2 @@
delete from `sys_params` where param_code = 'mqtt.signature_key';
INSERT INTO `sys_params` (id, param_code, param_value, value_type, param_type, remark) VALUES (120, 'mqtt.signature_key', 'null', 'string', 1, 'mqtt 密钥 配置');
@@ -303,3 +303,19 @@ databaseChangeLog:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202508131557.sql
- changeSet:
id: 202509080921
author: fan
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202509080921.sql
- changeSet:
id: 202509080927
author: fan
changes:
- sqlFile:
encoding: utf8
path: classpath:db/changelog/202509080927.sql