LoginSignup
3
2

More than 3 years have passed since last update.

ブラウザからAWSのIoT Coreにつなぐ

Last updated at Posted at 2021-01-14

背景

AWSのIoT Coreにブラウザから接続するのにクラスメソッドさんのAWS IoTのMQTT over WebSocketにHTMLから接続してみたが非常に参考になるのですが、Paho mqtt clientのバージョンが古いため、最新版(v1.1.0)で試しました。

最新版にするメリットは、reconnectの処理が実装されているらしいということです。
本記事はpaho-mqttを使用してみたところ、色々とハマりどころがありましたので、そのまとめです。

最初に結果

reconnectの試験をするために、次の2種類を試しましたが、結果は次のとおりでした。
- PCのWiFiをOFFする ⇒ 再接続してくれる
- PCをスリープさせる  ⇒ 再接続に失敗する
- reconnectが成功したときもsubscribeしてくれないので、改めてsubscribeする必要があります。

最新版(v1.1.0)を入手する

最新版はgithubで管理されています。
https://github.com/eclipse/paho.mqtt.javascript

使用方法

JavaScriptからrequireして利用したかったので、このようにしました。ここでのポイントは2つ。
- Paho.MQTT.clientではなくなっていて、Paho.clientにする必要がありました。

※動いたコードから抜き出したコードなので動かないかも...

const Paho = require("paho-mqtt/paho-mqtt-min.js");
const CryptoJS = require("crypto-js");
const moment = require("moment");
let connected = false;
let endpoint = createEndpoint(
    'ap-northeast-1', // Your Region
    'yourendpoint.iot.ap-northeast-1.amazonaws.com', // Require 'lowercamelcase'!!
    'YOUR_AWS_ACCESS_KEY',
    'YOUR_AWS_SECRET_ACCESS_KEY')
);
let clientId = 'your client id';
let client = new Paho.Client(endpoint,clientId);
client.onConnectionLost = onConnectionLost;
client.onMessageArrived = onMessageArrived;
let connectOptions = {
    useSSL: true,
    timeout: 3,
    mqttVersion: 4,
    reconnect: true,
    onSuccess: function() {
        if(connected !== true) {
            // 最初の接続時の処理
            console.log("onConnect");
            client.subscribe("/World");
        } else {
            // 再接続時の処理
            console.log("reconnect");
            client.subscribe("/World");
        }
    onFailure: function(e) {
        callback(e);
    }
};

client.connect(connectOptions);

function onConnectionLost(responseObject) {
  if (responseObject.errorCode !== 0)
    console.log("onConnectionLost:"+responseObject.errorMessage);
};
function onMessageArrived(message) {
  console.log("onMessageArrived:"+message.payloadString);
  client.disconnect(); 
};

function SigV4Utils(){}

SigV4Utils.sign = function(key, msg) {
    var hash = CryptoJS.HmacSHA256(msg, key);
    return hash.toString(CryptoJS.enc.Hex);
};

SigV4Utils.sha256 = function(msg) {
    var hash = CryptoJS.SHA256(msg);
    return hash.toString(CryptoJS.enc.Hex);
};

SigV4Utils.getSignatureKey = function(key, dateStamp, regionName, serviceName) {
    var kDate = CryptoJS.HmacSHA256(dateStamp, 'AWS4' + key);
    var kRegion = CryptoJS.HmacSHA256(regionName, kDate);
    var kService = CryptoJS.HmacSHA256(serviceName, kRegion);
    var kSigning = CryptoJS.HmacSHA256('aws4_request', kService);
    return kSigning;
};

function createEndpoint(regionName, awsIotEndpoint, accessKey, secretKey) {
    var time = moment.utc();
    var dateStamp = time.format('YYYYMMDD');
    var amzdate = dateStamp + 'T' + time.format('HHmmss') + 'Z';
    var service = 'iotdevicegateway';
    var region = regionName;
    var secretKey = secretKey;
    var accessKey = accessKey;
    var algorithm = 'AWS4-HMAC-SHA256';
    var method = 'GET';
    var canonicalUri = '/mqtt';
    var host = awsIotEndpoint;

    var credentialScope = dateStamp + '/' + region + '/' + service + '/' + 'aws4_request';
    var canonicalQuerystring = 'X-Amz-Algorithm=AWS4-HMAC-SHA256';
    canonicalQuerystring += '&X-Amz-Credential=' + encodeURIComponent(accessKey + '/' + credentialScope);
    canonicalQuerystring += '&X-Amz-Date=' + amzdate;
    canonicalQuerystring += '&X-Amz-SignedHeaders=host';

    var canonicalHeaders = 'host:' + host + '\n';
    var payloadHash = SigV4Utils.sha256('');
    var canonicalRequest = method + '\n' + canonicalUri + '\n' + canonicalQuerystring + '\n' + canonicalHeaders + '\nhost\n' + payloadHash;

    var stringToSign = algorithm + '\n' +  amzdate + '\n' +  credentialScope + '\n' +  SigV4Utils.sha256(canonicalRequest);
    var signingKey = SigV4Utils.getSignatureKey(secretKey, dateStamp, region, service);
    var signature = SigV4Utils.sign(signingKey, stringToSign);

    canonicalQuerystring += '&X-Amz-Signature=' + signature;
    return 'wss://' + host + canonicalUri + '?' + canonicalQuerystring;
}

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2