首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >卡在1004 -从Tuya API中无效的签名

卡在1004 -从Tuya API中无效的签名
EN

Stack Overflow用户
提问于 2022-07-21 07:31:04
回答 2查看 658关注 0票数 1

我尝试使用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的站点调用它们时相同。有效载荷也是一样的,但是响应是非常不同的。

在Tuya网站设备调试上的相同请求&在web应用程序中

添加sign_version:'2.0‘请求头或使用不同的url (const = /v1.0/iot-03/devices/${deviceId}/commands;)似乎没有帮助。

代码语言:javascript
复制
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
  };
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-07-27 15:40:26

看来你不会把身体传给签名方法了。整个请求都需要签署,包括任何机构。签名后,您不能更改请求的详细信息,除非添加标志标头。

可能值得将调用安排为三个步骤--第一步是构建请求对象。一个是根据整个请求对象添加签名头(因此它负责对正确的字段进行签名)。然后最后将其发送到httpClient.request进行呼叫。

我猜想在您的代码中还存在一些“尝试让它工作”的东西,例如将url设置为requestHeaders.path。我想你也需要一个时间戳头。所有这些都应该在文档中,或者查看Tuya的邮递员收藏的预请求脚本。

票数 0
EN

Stack Overflow用户

发布于 2022-10-12 04:47:34

他们的示例脚本有几个错误:

getDeviceInfo()

  • method设置为GET而不是POST
  • url设置为"/v1.0/iot-03/devices/${deviceId}/functions""/v1.0/iot-03/devices/${deviceId}/specification"
  • 最后是return data;,所以它得到输出。

这让它对我有用。

啊,这个答案跟他们今天的例子有关:

代码语言:javascript
复制
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}`);
});
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73062266

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档