ドキュメントには、言語に特化した詳しい説明があまりないので思った以上に苦労したので、その内容(主にJavaによる認証部分の実装手順)を備忘録代わりに残しておこうと思う。
(ただし、利用しているツール、ライブラリ、及びProduct Advertising APIの詳細な内容にはあまり触れないのでそれぞれのサイトを確認してください。)
まず、Product Advertising APIをSOAPによる呼び出し方法(認証の方式)には次の2種類がある。
- WS-Securityを利用せずにSOAPリクエストを処理する方法
- WS-Securityを利用してSOAPリクエストを処理する方法
本稿では、WS-Securityを利用してSOAPリクエストを処理する方法を説明する。この方法は、WS-Security1.0を利用し認証を行う。JDK単体ではWS-Seurityまでサポートされていないようなので、JAX-WSの実装としてApache CXFを利用する。また、署名に使用する証明書は、keytoolが公開鍵と秘密鍵が別になった証明書のインポートができない(ここは調査不足の可能性も)ようなので、amazonから提供される証明書ではなく、keytoolで生成した自己署名証明書を使用する。
なお、「WS-Securityを利用せずにSOAPリクエストを処理する方法」はこちらを参照。
今回使用したツール、ライブラリは以下の通りです。
ツール、ライブラリ名 | バージョン |
---|
Java Development Kit
7u51(1.7.0_51)
Eclipse IDE for Java EE Developers
4.3.1
Apache CXF
2.7.8
Tomcat
7.5.0
(1)自己署名証明書(秘密鍵・公開鍵のキーペア)の準備
amazonで有効なアルゴリズムはRSAなので、キーアルゴリズムとしてRSAを指定し、自己署名証明書(秘密鍵・公開鍵のキーペア)を生成する。有効期間は、amazonで生成可能な証明書の期間と同じ1年(365日)を設定している。
>keytool -genkeypair -keyalg RSA -dname <X.500識別名> -alias <キーエイリアス> -keypass <キーパスワード> -keystore <キーストアファイル> -storepass <キーストアパスワード> -validity 365
次に生成した自己署名証明書(秘密鍵・公開鍵のキーペア)から、amazonにアップロードするための公開鍵を同じくkeytoolを使用しRFC 1421 証明書符号化のフォーマット(-rfcオプション)で生成する。
>keytool -exportcert -alias <キーエイリアス> -keystore <キーストアファイル> -file <公開鍵ファイル> -storepass <キーストアパスワード> -rfc
生成された公開鍵ファイルは、AWS Management ConsoleのSecurity Credentialsからamazonにアップロードする。
- X.500識別名
- とりあえず適当に"cn=<名前(英字)>, c=JP"とか指定しておく。
- キーエイリアス
- 証明書(秘密鍵・公開鍵のキーペア)にアクセスするためのエイリアス名
- キーストアパスワード
- キーストアにアクセスするためのパスワード
- キーパスワード
- 証明書(秘密鍵)にアクセスするためのパスワード
- キーストアファイル
- 証明書(秘密鍵・公開鍵のキーペア)を格納するキーストアファイル
- キーストアファイル
- amazonにアップロードする公開鍵ファイル(RFC 1421 証明書符号化のフォーマット)
(2)EclipseにApache CXF、Tomcatを設定する
Apache CXFによりコード生成をするため、まずEclipseにApache CXFの設定を行う。また、クライアントコードを生成するだけなのに、なぜかDynamic web Project(Web Serverの実装がないと)でないとApache CXFのコード生成ができないようなのでTomcatも併せてEclipseに設定する。Apache CXFの設定
Apache CXFをダウンロードし、適当な場所に展開する。Eclipseの「Window」→「Preferences」→「Web Services」→「CXF 2.x Preferences」を開き、「add」ボタンを押下し、展開した場所を設定する。追加後、一覧に追加したApache CXFの実装がバージョンと共に表示されるので、追加したApache CXFにチェックを入れる。そのほかの設定はとりあえずデフォルトのままでよい。
Tomcatの設定
同様にTomcatダウンロードし、適当な場所に展開する。Eclipseの「Window」→「Preferences」→「Server」→「Runtime Environments」を開き、「add」ボタンを押下する。New Server Runtime Environmentが開くので「Apache」→「Apache Tomcat v7.0」を選択し、展開した場所を設定する。
追加後、一覧に追加したTomcatの実装がバージョンと共に表示される。
(3)WSDLからSOAPのクライアントコードを生成する
プロジェクト(Dynamic Web Project)の作成
クライアントコードを生成するためプロジェクトをEclipseのDynamic Web Projectとして作成し、プロジェクトのPropertiesを開き、「Java Build Path」の「Add Library」を押下し、「CXF Runtime」を追加する。追加後、一覧に追加したApache CXF Libraryが表示される。
クライアントコードの生成
Eclipseの「File」→「New」→「Other」を選択し、Newウィザードで「Web Services」→「Web Service Client」を選択する。「Web Service Client」ウィザードが開くので、「Server definition」にProduct Advertising APIのWSDLのURLを設定し、「Configuration」のServer Runtimeに「Tomcat v7.0 Server」、Web service runtimeに「Apache CXF 2.x」を設定し「Next」をクリックする。次に、WSDL2Javaの設定ウィザードが開き、自動生成されたパッケージ名が「com.amazon.webservices.awsecommerceservice.2011-08-01」となっており、このままだとJavaの規約違反となるため「com.amazon.webservices.awsecommerceservice._2011_08_01」に変更し「Finish」ボタンを押下する。
「Finish」ボタンを押下後、パッケージcom.amazon.webservices.awsecommerceservice._2011_08_01配下に次のソースが生成される。
Accessories.java Arguments.java AWSECommerceService.java AWSECommerceServicePortType.java AWSECommerceServicePortTypeImpl.java AWSECommerceServicePortTypeImpl1.java AWSECommerceServicePortTypeImpl10.java AWSECommerceServicePortTypeImpl2.java AWSECommerceServicePortTypeImpl3.java AWSECommerceServicePortTypeImpl4.java AWSECommerceServicePortTypeImpl5.java AWSECommerceServicePortTypeImpl6.java AWSECommerceServicePortTypeImpl7.java AWSECommerceServicePortTypeImpl8.java AWSECommerceServicePortTypeImpl9.java AWSECommerceServicePortType_AWSECommerceServicePortCA_Client.java AWSECommerceServicePortType_AWSECommerceServicePortCN_Client.java AWSECommerceServicePortType_AWSECommerceServicePortDE_Client.java AWSECommerceServicePortType_AWSECommerceServicePortES_Client.java AWSECommerceServicePortType_AWSECommerceServicePortFR_Client.java AWSECommerceServicePortType_AWSECommerceServicePortIN_Client.java AWSECommerceServicePortType_AWSECommerceServicePortIT_Client.java AWSECommerceServicePortType_AWSECommerceServicePortJP_Client.java AWSECommerceServicePortType_AWSECommerceServicePortUK_Client.java AWSECommerceServicePortType_AWSECommerceServicePortUS_Client.java AWSECommerceServicePortType_AWSECommerceServicePort_Client.java Bin.java BrowseNode.java BrowseNodeLookup.java BrowseNodeLookupRequest.java BrowseNodeLookupResponse.java BrowseNodes.java Cart.java CartAdd.java CartAddRequest.java CartAddResponse.java CartClear.java CartClearRequest.java CartClearResponse.java CartCreate.java CartCreateRequest.java CartCreateResponse.java CartGet.java CartGetRequest.java CartGetResponse.java CartItem.java CartItems.java CartModify.java CartModifyRequest.java CartModifyResponse.java Collections.java CorrectedQuery.java CustomerReviews.java DecimalWithUnits.java EditorialReview.java EditorialReviews.java Errors.java HTTPHeaders.java Image.java ImageSet.java Item.java ItemAttributes.java ItemLink.java ItemLinks.java ItemLookup.java ItemLookupRequest.java ItemLookupResponse.java Items.java ItemSearch.java ItemSearchRequest.java ItemSearchResponse.java LoyaltyPoints.java Merchant.java NewReleases.java NonNegativeIntegerWithUnits.java ObjectFactory.java Offer.java OfferAttributes.java OfferListing.java Offers.java OfferSummary.java OperationRequest.java OtherCategoriesSimilarProducts.java package-info.java Price.java Promotion.java Promotions.java Property.java RelatedItem.java RelatedItems.java Request.java SavedForLaterItems.java SearchBinSet.java SearchBinSets.java SearchResultsMap.java SimilarityLookup.java SimilarityLookupRequest.java SimilarityLookupResponse.java SimilarProducts.java SimilarViewedProducts.java StringWithUnits.java TopItemSet.java TopSellers.java Tracks.java VariationAttribute.java VariationDimensions.java Variations.java VariationSummary.java
(4)Product Advertising APIを呼び出す
Product Advertising APIを呼び出すときに、WS-Securityを利用するためのApache WSS4J用のプロパティファイル(client_sign.properties)と、暗号化を行う際に、(1)で生成した証明書(秘密鍵)へアクセスするためのキーパスワードを設定するコールバッククラス(ClientPasswordCallback)を先に作成する。Apache WSS4J用のプロパティファイル(client_sign.properties)
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=<キーストアパスワード> org.apache.ws.security.crypto.merlin.keystore.alias=<キーエイリアス> org.apache.ws.security.crypto.merlin.keystore.file=<キーストアファイル>
コールバッククラス(ClientPasswordCallback)クラス
import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import org.apache.ws.security.WSPasswordCallback; public class ClientPasswordCallback implements CallbackHandler { /** 証明書(秘密鍵)にアクセスするためのキーパスワード */ private static String keyPassword = "<キーパスワード>"; public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; // キーパスワードの設定 pc.setPassword(keyPassword); } }
最後にProduct Advertising APIを実行するmain()メソッドを持つクラスを実装する。
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.ws.WebServiceRef; import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor; import org.apache.ws.security.handler.WSHandlerConstants; import com.amazon.webservices.awsecommerceservice._2011_08_01.AWSECommerceService; import com.amazon.webservices.awsecommerceservice._2011_08_01.AWSECommerceServicePortType; import com.amazon.webservices.awsecommerceservice._2011_08_01.Item; import com.amazon.webservices.awsecommerceservice._2011_08_01.ItemSearch; import com.amazon.webservices.awsecommerceservice._2011_08_01.ItemSearchRequest; import com.amazon.webservices.awsecommerceservice._2011_08_01.ItemSearchResponse; public class AmazonService { @WebServiceRef private static AWSECommerceService AWSECommerceService; /** WS-Security(シグネチャのキー) */ private final static String WS_SECURITY_SIGNATURE_KEY_IDENTIFIER = "DirectReference"; /** WS-Security(シグネチャのキーアルゴリズム) */ private final static String WS_SECURITY_SIGNATURE_ALGORITHM = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; /** AWS アクセスキー識別子 */ private static String awsAccessKeyId = "xxxxxxxxxxxxxxxxxxxx"; /** amazonアソシエイトのアカウントID */ private static String associateTag = "XXXXXXXXXX-nn"; /** 共通のリクエストパラメータ(Validate) */ private static String validate = "False"; /** 共通のリクエストパラメータ(XMLEscaping) */ private static String xmlEscaping = "Single"; public static void main(String[] args) throws Exception { // サービスのエンドポイントの取得 AWSECommerceService = new AWSECommerceService(); AWSECommerceServicePortType endPoint = AWSECommerceService .getAWSECommerceServicePortJP(); Client client = ClientProxy.getClient(endPoint); // WS-Securityを使用するためのプロパティ設定 Map<String, Object> outProps = new HashMap<String, Object>(); outProps.put(WSHandlerConstants.USER, "<キーエイリアス>"); outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.TIMESTAMP + " " + WSHandlerConstants.SIGNATURE); outProps.put(WSHandlerConstants.SIG_KEY_ID, WS_SECURITY_SIGNATURE_KEY_IDENTIFIER); outProps.put(WSHandlerConstants.SIG_ALGO, WS_SECURITY_SIGNATURE_ALGORITHM); outProps.put(WSHandlerConstants.SIG_PROP_FILE, "client_sign.properties"); outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordCallback.class.getName()); WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps); client.getOutInterceptors().add(wssOut); // ItemSearch用のリクエストオブジェクトの生成 List<ItemSearchRequest> request = new ArrayList<ItemSearchRequest>(); String marketplaceDomain = null; ItemSearchRequest itemSearchRequest = new ItemSearchRequest(); itemSearchRequest.setKeywords("新居昭乃"); itemSearchRequest.setAvailability("Available"); itemSearchRequest.setSearchIndex("Blended"); itemSearchRequest.getResponseGroup().add("Medium"); itemSearchRequest.getResponseGroup().add("ItemAttributes"); request.add(itemSearchRequest); ItemSearch itemSearch = new ItemSearch(); itemSearch.setAssociateTag(associateTag); itemSearch.setAWSAccessKeyId(awsAccessKeyId); itemSearch.setMarketplaceDomain(marketplaceDomain); itemSearch.setXMLEscaping(xmlEscaping); itemSearch.setValidate(validate); itemSearch.getRequest().add(itemSearchRequest); // サービス(itemSearch)の実行 ItemSearchResponse itemSearchResponse = endPoint.itemSearch(itemSearch); // 検索結果の表示 for (Item item : itemSearchResponse.getItems().get(0).getItem()) { System.out.println(item.getItemAttributes().getTitle()); System.out.println("\t" + item.getDetailPageURL()); } } }
- 41~45行目
- (1)で生成したサービスクラスから、エンドポイント(ここでは日本サイト)を取得する。
- 47~60行目
- WS-Securityを使用するためのプロパティの設定する。WS-Securityのアクション(WSHandlerConstants.ACTION)には、WSHandlerConstants.TIMESTAMPとWSHandlerConstants.SIGNATUREを指定し、WS-Securityのユーザー(WSHandlerConstants.USER)には、(1)で生成したキーストアの証明書のキーエイリアスを指定し、シグネチャを生成するアルゴリズム(WSHandlerConstants.SIG_ALGO)には「http://www.w3.org/2000/09/xmldsig#rsa-sha1」を指定し、シグネチャーの識別子(WSHandlerConstants.SIG_KEY_ID)にはメッセージに埋め込む方式の「DirectReference」を指定する。
- 62~80行目
- itemSearchを呼び出すのに必要なオブジェクトを初期化・設定する。設定する内容はProduct Advertising APIを参照。
- 82~83行目
- itemSearchの実行
- 85~89行目
- 検索結果を表示
実際のSOAP呼び出しの際、SOAPメッセージに必要な要素がない場合、だいたい400:Bad Requestとなり、IDなどの認証情報に誤りがある場合403:Forbiddenとなる。
0 件のコメント:
コメントを投稿