ID-Blogger

Amazon Product Advertising APIの署名認証をAS3で行う

Flash CS3でActionscript3にスクリプトを拡張してimportさせるための設定

AmazonのProduct Advertising APIから貰ってこれる商品情報のデータを使ってごにょごにょするサイトを構築していたのですが、情報量の多さからUIまわりはjQuery UIを使ってAjaxらしく動的に処理する仕様で開発しておりました。
がしかし...70%くらいは完成したかな~という段階で各種ブラウザで動作確認を行ったところ、IE6で表示がメチャクチャになる事が発覚!!
IE6はWebデザイナーの大敵!ホント殺意を覚えるレベルです。

CSSハックすればいいんでしょうがjQuery UIが絡む要素があまりに深く複雑なので開発を断念。
もうブラウザ毎に表示を左右されないFlashでイチから作り直すことにしました。


ところが問題になってくるのがProduct Advertising APIの署名認証。これが涙が出るくらい複雑なんです...。
という事でだれかActionScript3.0(AS3)でソース作ってる人いないかな~とググリまくったところ、素晴らしいActionScriptを書いた方がいらっしゃいました♪


AS3でHMAC-SHA256とbase64エンコードを実装する準備

まずはAS3でHMAC-SHA256とbase64エンコードを実装するための準備をします。

Product Advertising APIの署名をFlash(AS3)で作る - HOBBY ROOM

 初めに、as3corelibをダウンロードし、パスを通しておきます。
 さらに、CS3の人はBase64Encoderが無いので、Flex3 SDKをダウンロードしてパスを通します。
 あとは、getAmazonRequestで以前のリクエストとシークレットキーを入れれば、タイムスタンプと並べ替えと署名を作ったものを返してくれます。

という事でHOBBY ROOMさんに書いてある通りGoogle Codeからas3corelibをダウンロードしてZIPを解凍します。
同じくCS3を使っているのでAdobeのサイトからFlex3 SDKをダウンロードしてZIPを解凍します。
Flex3 SDKをダウンロードする際にAdobe IDが必要なんですが、普通のAdobe IDと違ってデベロッパー用のものになるようです。
自分もAdobe IDを持っていたのですが再度登録し直してからダウンロードという手順になりました。


次にダウンロードしたクラスをActionScriptでimportするんですがここで躓きました。
ソースにimportがあって「パスを通しておき」と解説されてるので、CSSとかJavascriptみたいにフォルダへの階層を辿ってActionScriptに記述すればいいもんだと解釈していたのですがこれが間違い

Flashで新たにクラスを追加する場合、メニューから編集>環境設定>ActionScript>言語:ActionScript3.0設定を開いて、ここから該当するフォルダを選択してクラスパスを設定するようです。
自分の場合は解凍してできたas3corelibとFlex3 SDKの2つのフォルダをそのままFlash本体のフォルダに格納しました。

という事でクラスパスはこんな風になりました。(\は¥です)
as3corelib C:\Program Files\Adobe\Adobe Flash CS3\as3corelib-.92.1\as3corelib-.92.1\src
Flex3 SDK C:\Program Files\Adobe\Adobe Flash CS3\flex_sdk_3.4\frameworks\projects\framework\src

記事の頭にある画像が設定画面のキャプチャーになっています。
これで下準備は完了です。


ActionScriptソース

あとはHOBBY ROOMさんのサイトにあるActionScriptの通りで動作しますが少し手を加えてみました。
ステージ上にあるsearchStartボタンをクリックすると、Keywordsテキストボックスに入力された検索キーワードを取り込んで署名されたリクエストURLの結果requestUrlをtraceで吐き出すitemSearchという関数を追加してあります。

import com.adobe.crypto.*;
import mx.utils.Base64Encoder;
searchStart.addEventListener(MouseEvent.CLICK,itemSearch);//searchStartボタンクリックで検索開始
//検索
function itemSearch(event:MouseEvent):void {
	var secretKey:String = "○○○○○○";//秘密鍵。
	var AmazonUrl:String = "http://ecs.amazonaws.jp/onca/xml";
	var param:Array = [
	"Service=AWSECommerceService",
	"AWSAccessKeyId=○○○○○○○○○",//アクセスキー
	"Operation=ItemSearch",
	"Keywords=" + escapeMultiByte(Keywords.text)//Keywordsテキストボックスを取り込んでエスケープ
	];
	var params:String = AmazonUrl+"?"+param.join("&");
	var requestUrl:String = getAmazonRequest(secretKey,params);
	trace(requestUrl);//署名されたリクエスト結果
}
//APIのバージョンを2008-01-23から2009-07-01に変更
//以下HOBBY ROOMさんのソースのまま
//シークレットキー、リクエスト(URLエンコード済みのを)
function getAmazonRequest(s_key:String, re:String):String{
	var ar:Array=re.split('?');
	var domain:Array=getDomain(ar[0]);
	var para:Array=ar[1].split('&');
	var time:String='Timestamp='+getTimestamp();
	para.push(time);
	para.sort();
	var sha256:String=HMAC.hash(s_key, 'GET\n'+domain[0]+'\n'+domain[1]+'\n'+para.join('&'), SHA256);
	var byte:ByteArray=new ByteArray();
	for(var AA:int; AA < sha256.length; AA+=8){
		byte.writeInt(parseInt(sha256.substr(AA, 8), 16));
	}
	var sign:String=encBase64(byte);
	sign=sign.replace(/=/g, '%3D');
	sign=sign.replace(/\+/g, '%2B');
	sign=sign.replace(/\//g, '%2F');
	return re+'&'+time+'&Signature='+sign;
}
function getTimestamp():String{
	var date:Date=new Date();
	return date.getUTCFullYear()+'-'+hosei(date.getUTCMonth()+1)
		+'-'+hosei(date.getUTCDate())+'T'+hosei(date.getUTCHours())+'%3A'+hosei(date.getUTCMinutes())+'%3A'+hosei(date.getUTCSeconds())+'Z&Version=2009-07-01';//2008-01-23から変更してます。
	function hosei(n:int):String{
		if(n < 10){
			return '0'+n;
		}else{
			return String(n);
		}
	}
}
function getDomain(st:String):Array{
	var ar:Array=new Array();
	var i:int=st.indexOf('/', 7);
	ar.push(st.substr(7, i-7));
	ar.push(st.substr(i));
	return ar;
}
function encBase64(msg:ByteArray):String{
	var enc:Base64Encoder=new Base64Encoder();
	enc.insertNewLines=false;
	enc.encodeBytes(msg);
	return enc.flush();
}

itemSearch関数の部分にAmazonにリクエストするカテゴリを追加したりアソシエイトIDを入れたり任意に変えればいろいろ使えると思います。

ついでにもうひとつ躓いたところがあったんですが、受け取ったキーワードは必ずescapeMultiByteでエスケープしないとダメです。
またリクエストにレスポンスグループなんかを含めるときに「ResponseGroup=<グループ1>,<グループ2>,<グループn>」というのを追加すると思いますが、この場合も「,」が引っかかるのでescapeでエスケープしてから渡してください。


これでProduct Advertising APIからXMLの結果が返って来るんですが、今度はこれをXMLパーサーしなきゃイケなくてこれがまた大変で...
その辺はまた次の機会に書きたいと思います。

断念する前のPHPで署名認証する方法もそのうち載せられればと思います。