API权限检验

一、消息体签名

消息体签名检验是开发者与 SENSORO IoT 云平台相互请求的鉴权方式。

开发者提交 Webhook(自定义回调URL) 地址后,SENSORO 发送给开发者的请求设置了 3 个 HTTP Header, 包括 X-ACCESS-ID, X-ACCESS-NONCEX-ACCESS-SIGNATURE。开发者通过检验 signature 对请求进行校验, 来确认此次 POST 请求来自 SENSORO 服务器。

开发者请求 SENSORO IoT 开放 API 接口时,也需要携带以上三个 headers 参数,SENSORO 服务器通过检验 signature 对请求进行校验,来确定开发者身份。

请求携带 headers 参数如下表所示:

键名 名称 说明
X-ACCESS-ID AppID 开发者应用标示符
X-ACCESS-NONCE AppSecret 发请求的 Unix 时间,精确到毫秒
X-ACCESS-SIGNATURE 以 AppSecret 作为密钥,计算 X-ACCESS-NONCE + GET POST PUT DELETE... + 请求的完整 URL + 请求的 Body(无则留空{})的 HMAC(SHA256) 的结果。

开放 API 可使用 https://iot.sensoro.com “开发者” 页面内任意推送服务秘钥参数, 推送服务秘钥在系统中的位置如下图:

  • 示例1, Webhook 接收端签名校验示例

    NodeJS 示例:

    var crypto = require('crypto'); var SECRET = "YOURAPPSECRET"; var error = new Error('Invalid signature'); error.status = 400; function verifySignatrue(req) { var url = req.protocol + '://' + req.get('host') + req.originalUrl; // 生成 ACCESS_NONCE,值为当前的 Unix 时间,单位为毫秒 var ACCESS_NONCE = req.headers['x-access-nonce']; var BodyRaw = ''; if (req.body && req.body instanceof Object) { try{ BodyRaw = JSON.stringify(req.body); }catch(e) { console.error(e); } } var original = new Buffer(ACCESS_NONCE + req.method.toUpperCase() + url + BodyRaw); var secret = crypto.createHmac('SHA256', SECRET).update(original).digest('base64'); if (req.headers['x-access-signature'] !== secret) { return callback(error); } callback(); } module.exports = function() { return function(req, res, next) { verifySignatrue(req, next); }; };

  • 示例2, Open API 获取传感器基本信息

    NodeJS 示例:

    var crypto = require('crypto'); var urllib = require('urllib'); var appID = '9yCs1XmRya2T'; var appSecret = 'MKLFSYfBgZJgdCNsN3xGdmKZBi6bRXi0'; function getDeviceInfo(callback) { var method = 'GET'; var nonce = 1500444830886; //Date.now(); var url = "https://iot-api.sensoro.com/developers/device/10900117C640F19D"; var signature = getSignature(appSecret, nonce, method, url, {}); var opt = { method: method, headers: { 'X-ACCESS-ID': appID, 'X-ACCESS-NONCE': nonce, 'X-ACCESS-SIGNATURE': signature } }; urllib.request(url, opt, callback); } function getSignature(appSecret, nonce, method, url, body){ var original = nonce + method + url + JSON.stringify(body); return crypto.createHmac('SHA256', appSecret) .update(new Buffer(original)).digest('base64'); } getDeviceInfo(function(err, ret){ console.info(err, ret); });

    curl 示例:

    
      curl -X GET 'https://iot-api.sensoro.com/developers/device/10900117C640F19D' -H 'x-access-id: 9yCs1XmRya2T' -H 'x-access-nonce: 1500444830886' -H 'x-access-signature: EBxaJU+SdbBKPfyqdlEY+9P0dN6VieuMUd/JGEwRbgo='
    
  • 示例3, Open API 传感器传输周期设定

    NodeJS 示例:

var crypto = require('crypto'); var urllib = require('urllib'); var appID = '9yCs1XmRya2T'; var appSecret = 'MKLFSYfBgZJgdCNsN3xGdmKZBi6bRXi0'; function setDeviceInterval(callback) { var method = 'POST'; var nonce = 1500444830886; //Date.now(); var url = "https://iot-api.sensoro.com/developers/device/interval"; var body = { sns: ['10900117C640F19D'], cfg: { interval: 600 } }; var signature = getSignature(appSecret, nonce, method, url, body); var opt = { method: method, contentType: 'json', data: body, headers: { 'X-ACCESS-ID': appID, 'X-ACCESS-NONCE': nonce, 'X-ACCESS-SIGNATURE': signature } }; urllib.request(url, opt, callback); } function getSignature(appSecret, nonce, method, url, body){ var original = nonce + method + url + JSON.stringify(body); return crypto.createHmac('SHA256', appSecret) .update(new Buffer(original)).digest('base64'); } setDeviceInterval(function(err, ret){ console.info(err, JSON.parse(ret)); });

curl 示例:


    curl -X POST 'https://iot-api.sensoro.com/developers/device/interval' -H 'x-access-id: 9yCs1XmRya2T' -H 'x-access-nonce: 1500444830886' -H 'x-access-signature: LrJg8MXMi5mCjzoiwOR1QuvZq6mp+6oVMtDBk5GQPs0=' -H 'content-type: application/json' -d '{"sns": ["10900117C640F19D"], "cfg": {"interval": 600 } }'
  

二、消息体加密

消息加密解密技术方案基于 AES 加解密算法来实现,具体如下:

  1. App Key 即消息加解密Key,长度固定为43个字符,从a-z,A-Z,0-9共62个字符中选取。由开发者在开发配置中填写,后也可修改。
  2. AES密钥: AESKey = Base64_Decode( AppKey + “=”),AppKey 尾部填充一个字符的 “=”, 用 Base64_Decode 生成 32 个字节的 AESKey;
  3. AES 采用 CBC 模式,秘钥长度为 32 个字节(256位),数据采用 PKCS#7 填充; PKCS#7:K 为秘钥字节数(采用32),buf 为待加密的内容,N 为其字节数。Buf 需要被填充为 K 的整数倍。在 buf 的尾部填充 (K-N%K) 个字节,每个字节的内容 是 (K- N%K)。
  4. BASE64 采用 MIME 格式,字符包括大小写字母各 26 个,加上 10 个数字,和加号 “+”,斜杠 “/”,一共 64 个字符,等号 “=” 用作后缀填充;

开发者可以下载开源的 NPM,安装使用,参考示例如下。

var MsgCrypt = require('wechat-crypto'); var APPSECRET = 'YOUR_APPSECRET', APPKEY = 'YOUR_APPKEY', APPID = 'YOUR_APPID'; var crypter = new MsgCrypt(APPSECRET, APPKEY, APPID); /** * 对密文进行解密 * * @param {String} text 待解密的密文 */ var encryptData = crypter.encrypt('YOUR_DATA'); /** * 对密文进行解密 * * @param {String} text 待解密的密文 */ var decryptData = crypter.decrypt(encryptData); console.log(decryptData);