2013-12-19

ApexからSNSを使ってAndroidにPush Notification!

前回はAWSコンソールからPush Notificationしましたが、API経由じゃないと何の旨味もないので、Apexを使ってPush Notificationしてみます。

SNS REST API リファレンス→http://docs.aws.amazon.com/sns/latest/api/Welcome.html

以下サンプル

public with sharing class SNSCommon {
    /**
     * AccessKey
     */
    public String AWSAccessKeyId = 'Input Your Access Key Id';

    /**
     * SecretKey
     */
    public String AWSSecretKeyId = 'Input Your Access Key Secret';

    //Publishする(us-east region)
    public String publish(String message) {
        Map<String, String> params = new Map<String, String>{
            'Action' => 'Publish',
            'AWSAccessKeyId' => this.AWSAccessKeyId,
            'SignatureMethod' => 'HmacSHA256',
            'SignatureVersion' => '2',
            'Message' => message,
//            'TargetArn' => 'Input Your Target Arn',
            'TopicArn' => 'Input Your Topic Arn',
            'Timestamp' => DateTime.now().format('YYYY-MM-dd') + 'T' + 
            DateTime.now().format('HH:mm:ssZ')
        };
        params.put('Signature', this.createSignature('POST', 'sns.us-east-1.amazonaws.com', '/', params));

        HttpRequest req = new HttpRequest();
        req.setEndpoint('https://sns.us-east-1.amazonaws.com');
        req.setMethod('POST');

        req.setBody(Utility.getSortedParam(params));
        Http http = new Http();
        HTTPResponse res = http.send(req);

        return res.getBody();
    }

    //signature生成
    private String createSignature(
        String method,
        String hostname,
        String resource,
        Map<String, String> params
    ) {
        String signature = 
            method + Constants.LF +
            hostname + Constants.LF +
            resource + Constants.LF +
            Utility.getSortedParam(params);
        System.debug(signature);
        return String.valueOf(EncodingUtil.base64Encode(
            Crypto.generateMac(
                'hmacSHA256', 
                Blob.valueOf(signature),
                Blob.valueOf(this.AWSSecretKeyId)
            )
        ));
    }
}

public without sharing class Utility {
    //セットをソートしてリストにする
    public static List<String> getSortedList(Set<String> keys) {
        List<String> sortedList = new List<String>();
        sortedList.addAll(keys);
        sortedList.sort();
        return sortedList;
    }

    /**
     * key=value&...でつないだ文字列を返却
     * @param mapParam 対象文字列=>文字列マップ
     * @return key=value&...でつないだ文字列
     */
    public static String getSortedParam(Map<String,String> mapParam){
        if (mapParam == null || mapParam.keySet().isEmpty()) {
            return '';
        }
        String param = '';
        List<String> sortedKey = getSortedList(mapParam.keySet());
        for(String key: sortedKey){
            param += percentEncodeRfc3986(key) + '=' + percentEncodeRfc3986(mapParam.get(key)) + '&';
        }
        return param.substring(0,param.length() - 1);
    }

    //Rfc3986に則ったURLエンコード
    public static String percentEncodeRfc3986(String s) {
        return EncodingUtil.urlEncode(s, 'UTF-8')
                    .replace('+', '%20')
                    .replace('*', '%2A')    
                    .replace('%7E', '~');
    }
}

signature versionは2を使ってます。

http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html

 

実装方法はサンプル通りで

[URL Method] + \n
[Http Header Host Name] + \n
[Resource] + \n
[キー値でソートしたURLパラメータ]

の文字列をhamc-sha1かhmac-sha256でAWS Secret Key使ってハッシュ化してbase64エンコードするだけ。

[キー値でソートしたURLパラメータ]の部分は普通のURLエンコードではなくて、RFC 3986 という規格に則ってエンコードする必要があります。

 

参考URL:http://docs.aws.amazon.com/AWSECommerceService/latest/DG/AuthJavaSampleSig2.html

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