2014-01-04

SNSのHTTP通知のハンドラーをforce.com Sitesで作ってみる。

SNSのHTTP通知ハンドラーをforce.com Sitesで作って、HTTP通知の中でTwilio API呼び出してSMS送信するような処理を作ってみました。

SNSからSMS送信できるのは米国の電話番号だけっぽいので日本の電話番号に対応できるように、Twilio API経由でSMS送信するっていうのが目的。

流れとしては以下になります。

  1. Apex RESTクラスを作成
  2. Sitesの有効なApexクラスに1のクラスを追加
  3. SNSでHTTP EndpointをSitesのURL(Apex REST)に設定し、Endpoint側でSubscribeする。 Endpointは下記サンプルクラスの場合はhttps://[SiteDomain]//services/apexrest/MyRestContextExampleになります。 ※HTTP Endpointに確認メッセージが送られるので、Salesforce側でデバッグするなりDBに格納するなりで メッセージ内容を取得し、メッセージ内のSubscribeURLにアクセスすると、当EndpointがSNS側に登録されるっていう流れ。
  4. consoleからpublish
※今回はSitesのanonymousなREST APIを使用するので、この記事を参考ください!

1のクラスはこんな感じ

@RestResource(urlMapping='/MyRestContextExample/*')
global with sharing class MyRestContextExample {    
    @HttpPost
    global static Boolean doPost() {
        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;

        Map<String, Object> notificationParams = (Map<String, Object>)JSON.deserializeUntyped(req.requestBody.ToString());

        if (String.valueOf(notificationParams.get('Type')) == 'Notification') {
	        if (notificationParams.containsKey('Message')) {
	            CallTwilio.sendSMS(String.valueOf(notificationParams.get('Message')));
	        }
        }
        system.debug(req.requestBody.ToString());
        system.debug(req.headers);
        system.debug(req.params);
        system.debug(req.resourcePath);
        return true;
    }

}

public with sharing class CallTwilio {
    public static void sendSMS(String sms_message) {
        TwilioRestClient client = TwilioAPI.getDefaultClient();
        TwilioMessage message = client.getAccount().getMessages().create(new Map<String, String>{
           'To' => '+*********',
           'From' => '+*********',
           'Body' => sms_message
        });
    }
}

本当はMessageのSignatureの検証をする必要がありますが、SNSのAWSからのHttp Notificationは単純に共通鍵方式ではなく、公開鍵で検証する方式になります。が、Apexには公開鍵でverifyするメソッドがなかったので断念。

この検証をしないと、偽装されたNotificationに対してもメッセージを処理しちゃいます。ということで、何らかの対策は必要で、思いつく限りはMessageとかSubjectに自前のSignature埋め込むぐらい?

MessageIDが使えれば

  1. API経由でNotification
  2. ResponseのMessageIDをDBに格納
  3. 受け取ったデバイスでDBに対象MessageIDがDBに格納されているかを検証
みたいな流れでいけそうですが、MessageIDが変わったりDBに格納される前にHTTP通知されちゃうとアウト。

RESTクラスを作ったら、あとはSites登録してconsoleからNotificationするだけです。最初のSubscription登録のときだけはSalesforceのデバッグを確認して、指定HTTP Endpointに対してSubscribeする必要があります。

これで、ひとつのTopicに対するPublishで特定メールアドレスにメールを送ったり、SMSを送信できます。

参考URL

このエントリーをはてなブックマークに追加