mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-31 03:43:57 +08:00
104 lines
3.4 KiB
JavaScript
104 lines
3.4 KiB
JavaScript
require('dotenv').config();
|
||||
|
|
const crypto = require('crypto');
|
|||
|
|
|
|||
|
|
|
|||
|
|
function generatePasswordSignature(content, secretKey) {
|
|||
|
|
// Create an HMAC object using SHA256 and the secretKey
|
|||
|
|
const hmac = crypto.createHmac('sha256', secretKey);
|
|||
|
|
|
|||
|
|
// Update the HMAC object with the clientId
|
|||
|
|
hmac.update(content);
|
|||
|
|
|
|||
|
|
// Generate the HMAC digest in binary format
|
|||
|
|
const binarySignature = hmac.digest();
|
|||
|
|
|
|||
|
|
// Encode the binary signature to Base64
|
|||
|
|
const base64Signature = binarySignature.toString('base64');
|
|||
|
|
|
|||
|
|
return base64Signature;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function validateMqttCredentials(clientId, username, password) {
|
|||
|
|
// 验证密码签名
|
|||
|
|
const signatureKey = process.env.MQTT_SIGNATURE_KEY;
|
|||
|
|
if (signatureKey) {
|
|||
|
|
const expectedSignature = generatePasswordSignature(clientId + '|' + username, signatureKey);
|
|||
|
|
if (password !== expectedSignature) {
|
|||
|
|
throw new Error('密码签名验证失败');
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
console.warn('缺少MQTT_SIGNATURE_KEY环境变量,跳过密码签名验证');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证clientId
|
|||
|
|
if (!clientId || typeof clientId !== 'string') {
|
|||
|
|
throw new Error('clientId必须是非空字符串');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证clientId格式(必须包含@@@分隔符)
|
|||
|
|
const clientIdParts = clientId.split('@@@');
|
|||
|
|
// 新版本 MQTT 参数
|
|||
|
|
if (clientIdParts.length !== 3) {
|
|||
|
|
throw new Error('clientId格式错误,必须包含@@@分隔符');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证username
|
|||
|
|
if (!username || typeof username !== 'string') {
|
|||
|
|
throw new Error('username必须是非空字符串');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 尝试解码username(应该是base64编码的JSON)
|
|||
|
|
let userData;
|
|||
|
|
try {
|
|||
|
|
const decodedUsername = Buffer.from(username, 'base64').toString();
|
|||
|
|
userData = JSON.parse(decodedUsername);
|
|||
|
|
} catch (error) {
|
|||
|
|
throw new Error('username不是有效的base64编码JSON');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解析clientId中的信息
|
|||
|
|
const [groupId, macAddress, uuid] = clientIdParts;
|
|||
|
|
|
|||
|
|
// 如果验证成功,返回解析后的有用信息
|
|||
|
|
return {
|
|||
|
|
groupId,
|
|||
|
|
macAddress: macAddress.replace(/_/g, ':'),
|
|||
|
|
uuid,
|
|||
|
|
userData
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function generateMqttConfig(groupId, macAddress, uuid, userData) {
|
|||
|
|
const endpoint = process.env.MQTT_ENDPOINT;
|
|||
|
|
const signatureKey = process.env.MQTT_SIGNATURE_KEY;
|
|||
|
|
if (!signatureKey) {
|
|||
|
|
console.warn('No signature key, skip generating MQTT config');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const deviceIdNoColon = macAddress.replace(/:/g, '_');
|
|||
|
|
const clientId = `${groupId}@@@${deviceIdNoColon}@@@${uuid}`;
|
|||
|
|
const username = Buffer.from(JSON.stringify(userData)).toString('base64');
|
|||
|
|
const password = generatePasswordSignature(clientId + '|' + username, signatureKey);
|
|||
|
|
return {
|
|||
|
|
endpoint,
|
|||
|
|
port: 8883,
|
|||
|
|
client_id: clientId,
|
|||
|
|
username,
|
|||
|
|
password,
|
|||
|
|
publish_topic: 'device-server',
|
|||
|
|
subscribe_topic: 'null' // 旧版本固件不返回此字段会出错
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = {
|
|||
|
|
generateMqttConfig,
|
|||
|
|
validateMqttCredentials
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (require.main === module) {
|
|||
|
|
const config = generateMqttConfig('GID_test', '11:22:33:44:55:66', '36c98363-3656-43cb-a00f-8bced2391a90', { ip: '222.222.222.222' });
|
|||
|
|
console.log('config', config);
|
|||
|
|
const credentials = validateMqttCredentials(config.client_id, config.username, config.password);
|
|||
|
|
console.log('credentials', credentials);
|
|||
|
|
}
|