我尝试使用Tuya IoT开发平台控制我的GoSund智能套接字,但是当我试图切换它的状态时,我仍然坚持这个错误响应:
{“代码”:1004,“msg”:“签名无效”,“成功”:false,"t":1658384161392,"tid":"97e938e608bc11eda4f0322e56e3d437"}
下面的代码基本上是用我的密钥和deviceId粘贴(https://developer.tuya.com/en/docs/iot/singnature?id=Ka43a5mtx1gsc)从官方的Tuya站点上略微修改的开发代码示例的副本。
当我试图使用Tuya的站点调试设备选项做完全相同的事情时,它只是起作用了。当我试图在web应用程序中使用他们的代码示例时,1004就失败了。除了每次调用它时都是新的标记之外,基本上所有请求头都与从Tuya的站点调用它们时相同。有效载荷也是一样的,但是响应是非常不同的。
添加sign_version:'2.0‘请求头或使用不同的url (const = /v1.0/iot-03/devices/${deviceId}/commands;)似乎没有帮助。
const config = {
/* openapi host */
//host: 'https://openapi.tuyacn.com',
host: 'https://openapi.tuyaeu.com',
/* fetch from openapi platform */
accessKey: 'I pasted here my Access ID/Client ID from iot.tuya.com',
/* fetch from openapi platform */
secretKey: 'I pasted here my Access Secret/Client Secret from iot.tuya.com',
/* Interface example device_ID */
deviceId: 'I pasted here Device ID of my GoSund smart plug',
};
const httpClient = axios.create({
baseURL: config.host,
timeout: 5 * 1e3,
});
async main(switchValue: boolean) {
try{
await this.getToken();
const data = await this.getDeviceInfo(config.deviceId, switchValue);
console.log('fetch success: ', JSON.stringify(data));
}catch(error){
console.log(error);
}
}
/**
* fetch highway login token
*/
async getToken() {
const method = 'GET';
const timestamp = Date.now().toString();
const signUrl = '/v1.0/token?grant_type=1';
const contentHash = crypto.createHash('sha256').update('').digest('hex');
const stringToSign = [method, contentHash, '', signUrl].join('\n');
const signStr = config.accessKey + timestamp + stringToSign;
const headers = {
t: timestamp,
sign_method: 'HMAC-SHA256',
client_id: config.accessKey,
sign: await this.encryptStr(signStr, config.secretKey),
};
const { data: login } = await httpClient.get('/v1.0/token?grant_type=1', { headers });
if (!login || !login.success) {
throw Error(`fetch failed: ${login.msg}`);
}
this.setState({ token: login.result.access_token })
}
/**
* fetch highway business data
*/
async getDeviceInfo(deviceId: string, switchValue: boolean) {
const query = {};
const method = 'POST';
const url = `/v1.0/devices/${deviceId}/commands`;
const reqHeaders: { [k: string]: string } = await this.getRequestSign(url, method, {}, query);
const { data } = await httpClient.request({
method,
data: {commands: [{code: "countdown_1", value: 0}, {code: "switch", value: switchValue}]},
params: {},
headers: reqHeaders,
url: reqHeaders.path,
});
if (!data || !data.success) {
throw Error(`request api failed: ${data.msg}`);
}
}
/**
* HMAC-SHA256 crypto function
*/
async encryptStr(str: string, secret: string): Promise<string> {
return crypto.createHmac('sha256', secret).update(str, 'utf8').digest('hex').toUpperCase();
}
/**
* request sign, save headers
* @param path
* @param method
* @param headers
* @param query
* @param body
*/
async getRequestSign(
path: string,
method: string,
headers: { [k: string]: string } = {},
query: { [k: string]: any } = {},
body: { [k: string]: any } = {},
) {
const t = Date.now().toString();
const [uri, pathQuery] = path.split('?');
const queryMerged = Object.assign(query, qs.parse(pathQuery));
const sortedQuery: { [k: string]: string } = {};
Object.keys(queryMerged)
.sort()
.forEach((i) => (sortedQuery[i] = query[i]));
const querystring = decodeURIComponent(qs.stringify(sortedQuery));
const url = querystring ? `${uri}?${querystring}` : uri;
const contentHash = crypto.createHash('sha256').update(JSON.stringify(body)).digest('hex');
const client_id = config.accessKey
const access_token = this.state.token
const stringToSign = [method, contentHash, '', url].join('\n');
const signStr = client_id + access_token + t + stringToSign;
return {
t,
path: url,
client_id: config.accessKey,
sign: await this.encryptStr(signStr, config.secretKey),
sign_method: 'HMAC-SHA256',
sign_version: '2.0',
access_token: access_token
};
}发布于 2022-07-27 15:40:26
看来你不会把身体传给签名方法了。整个请求都需要签署,包括任何机构。签名后,您不能更改请求的详细信息,除非添加标志标头。
可能值得将调用安排为三个步骤--第一步是构建请求对象。一个是根据整个请求对象添加签名头(因此它负责对正确的字段进行签名)。然后最后将其发送到httpClient.request进行呼叫。
我猜想在您的代码中还存在一些“尝试让它工作”的东西,例如将url设置为requestHeaders.path。我想你也需要一个时间戳头。所有这些都应该在文档中,或者查看Tuya的邮递员收藏的预请求脚本。
发布于 2022-10-12 04:47:34
他们的示例脚本有几个错误:
在getDeviceInfo()中
method设置为GET而不是POSTurl设置为"/v1.0/iot-03/devices/${deviceId}/functions"或"/v1.0/iot-03/devices/${deviceId}/specification"return data;,所以它得到输出。这让它对我有用。
啊,这个答案跟他们今天的例子有关:
import * as qs from 'qs';
import * as crypto from 'crypto';
import { default as axios } from 'axios';
let token = '';
const config = {
/* openapi host */
host: 'https://openapi.tuyacn.com',
/* fetch from openapi platform */
accessKey: '',
/* fetch from openapi platform */
secretKey: '',
/* Interface example device_ID */
deviceId: '',
};
const httpClient = axios.create({
baseURL: config.host,
timeout: 5 * 1e3,
});
async function main() {
await getToken();
const data = await getDeviceInfo(config.deviceId);
console.log('fetch success: ', JSON.stringify(data));
}
/**
* fetch highway login token
*/
async function getToken() {
const method = 'GET';
const timestamp = Date.now().toString();
const signUrl = '/v1.0/token?grant_type=1';
const contentHash = crypto.createHash('sha256').update('').digest('hex');
const stringToSign = [method, contentHash, '', signUrl].join('\n');
const signStr = config.accessKey + timestamp + stringToSign;
const headers = {
t: timestamp,
sign_method: 'HMAC-SHA256',
client_id: config.accessKey,
sign: await encryptStr(signStr, config.secretKey),
};
const { data: login } = await httpClient.get('/v1.0/token?grant_type=1', { headers });
if (!login || !login.success) {
throw Error(`fetch failed: ${login.msg}`);
}
token = login.result.access_token;
}
/**
* fetch highway business data
*/
async function getDeviceInfo(deviceId: string) {
const query = {};
const method = 'POST';
const url = `/v1.0/devices/${deviceId}/commands`;
const reqHeaders: { [k: string]: string } = await getRequestSign(url, method, {}, query);
const { data } = await httpClient.request({
method,
data: {},
params: {},
headers: reqHeaders,
url: reqHeaders.path,
});
if (!data || !data.success) {
throw Error(`request api failed: ${data.msg}`);
}
}
/**
* HMAC-SHA256 crypto function
*/
async function encryptStr(str: string, secret: string): Promise<string> {
return crypto.createHmac('sha256', secret).update(str, 'utf8').digest('hex').toUpperCase();
}
/**
* request sign, save headers
* @param path
* @param method
* @param headers
* @param query
* @param body
*/
async function getRequestSign(
path: string,
method: string,
headers: { [k: string]: string } = {},
query: { [k: string]: any } = {},
body: { [k: string]: any } = {},
) {
const t = Date.now().toString();
const [uri, pathQuery] = path.split('?');
const queryMerged = Object.assign(query, qs.parse(pathQuery));
const sortedQuery: { [k: string]: string } = {};
Object.keys(queryMerged)
.sort()
.forEach((i) => (sortedQuery[i] = query[i]));
const querystring = decodeURIComponent(qs.stringify(sortedQuery));
const url = querystring ? `${uri}?${querystring}` : uri;
const contentHash = crypto.createHash('sha256').update(JSON.stringify(body)).digest('hex');
const stringToSign = [method, contentHash, '', url].join('\n');
const signStr = config.accessKey + token + t + stringToSign;
return {
t,
path: url,
client_id: config.accessKey,
sign: await encryptStr(signStr, config.secretKey),
sign_method: 'HMAC-SHA256',
access_token: token,
};
}
main().catch(err => {
throw Error(`error: ${err}`);
});https://stackoverflow.com/questions/73062266
复制相似问题