NCMB(NiftyCloud MobileBackend)のREST APIをphpから直接呼び出す記事。
・NCMBとは
MBaaS(Mobile Backend as a Serviceの略)のひとつ。サーバーを運用せずともクラウド上の機能を呼び出すことでモバイルアプリが開発できるという優れものである。
・APIとは
そのクラウド上の機能を呼び出す役目をするのがAPI(Application Programming Interface)である。特にREST(REpresentational State Transfer) API は、セッションの状態に影響されない(ステートレス)なので、シンプルな操作ができる。NCMBはREST APIを提供しているので、今回はこれをphpから呼び出す。
・SDK?知らない子ですね
NCMBのREST APIは、Unity、iOS、Android、JavaScriptの環境で使えるSDKを公開している。これはAPIをさらに扱いやすくするツールである。今回はアプリではなくWebページでNCMBのデータを呼び出したいので、SDKを使おうと思えばJavaScriptを使うのが妥当に思えるが、クライアントサイドで処理するJavaScriptで書くとソースコードが丸見えなので、認証に使ったりする秘密鍵がモロバレになってしまうのだ(セキュリティもクソもあったものではない)。というわけで、サーバーサイドで処理できるphpでなんとかAPIを使いたいということでやってみた。
・curl
curlはHTTPなどの転送プロトコルのコマンドなどを提供するツールで、phpなど複数の言語に組み込まれている。
NCMBのREST APIのドキュメントを読むと、このcurlコマンドが使われているのでこれをphpで書いていく。今回はGETメソッドでオブジェクトの検索を例にとってやってみる。
・サンプルコマンド
curl -X GET -G \
-H "X-NCMB-Application-Key:549116a86b0ebbec4832d4086a56f36c82a5d64bc6528fa5e6220be76db5ef45" \
-H "X-NCMB-Timestamp:2013-09-10T02:44:35.452Z" \
-H "X-NCMB-Signature: bqPSP/7iXvjDRhrsSX9zasAfvgqvyeAKMJIU3/yLaX4=" \
-H "Content-Type: application/json" \
--data-urlencode 'where={"name":"curl"}' \
https://mb.api.cloud.nifty.com/2013-09-01/classes/cmd
これはNCMBのドキュメントから引っ張ってきたサンプルである。
まず、1行目の -X GET -G
でGETメソッドを指定している。
続いて2~5行目にわたる -H
は、HTTPヘッダの情報を指定している。
6行目の--data-urlencode
は、オブジェクトの検索条件を表すクエリ ストリングだ。
最後の1行がリクエスト送信先のURLになる。
で、これがphpのcurlだとどうなるのかというと、こうなる。
$ch=curl_init(); //curl初期化
$opt=array(
CURLOPT_URL=>"$url?$query", //URLとクエリストリング
CURLOPT_HTTPHEADER=>array( //HTTPヘッダ
"X-NCMB-Application-Key:$appkey",
"X-NCMB-Timestamp:$timestamp",
"X-NCMB-Signature:$signature",
"Content-Type: application/json",
),
CURLOPT_RETURNTRANSFER=>true
);
curl_setopt_array($ch,$opt); //オプションを設定
$res=curl_exec($ch); //リクエスト送信
curl_close($ch);
まず、phpではデフォルトがGETメソッドなので特に指定しない。
で、CURLOPT_HTTPHEADERに指定している配列がHTTPヘッダの情報になる。
クエリ ストリングは、URLの後ろに?で結合する。
まあ、curl関係はたったこれだけなのだが、面倒臭いのはここからである。
・シグネチャ
リクエストが正当なものであることを証明するための署名がシグネチャである。X-NCMB-Signatureで指定する。
このシグネチャを生成するのが難関である。手順としては、
①署名方式、署名のバージョン、タイムスタンプ、アプリケーションキー、クエリストリングを&で結合した文字列を生成する。
②リクエストメソッド、完全修飾ドメイン名(FQDN)、クラスパス、および①を\nで結合した文字列を生成する。
③②を、指定した署名方式で署名する。
④③を、Base64でエンコードする。
となる。
先ず①については、署名方式はHmacSHA256、バージョンは2である。タイムスタンプはその場で適当に錬成する。
$timestamp=date("Y-m-d\Th:i:s.v\Z"); // "YYYY-MM-DDTHH:mm:ss.sssZ"の形式にする
$str="SignatureMethod=HmacSHA256&SignatureVersion=2&X-NCMB-Application-Key=$appkey&X-NCMB-Timestamp=$timestamp&$query";
ここで気を付けるべきは、クエリ ストリングはキー名が昇順にソートされていて、かつJSONの部分はURLエンコードされている必要があることだ。たとえば、クエリ ストリングが
where={"name":"foo"}&include=usr&order=-score
とかだった場合、キーが昇順なので
include,order,where
の順で並んでいないといけないし、
{"name":"foo"}
はエンコードされて
%7B%22name%22%3A%22foo%22%7D
になっていないといけないので、最終的にはクエリストリングは
include=usr&order=-score&where=%7B%22name%22%3A%22foo%22%7D
になっていないといけない。
ここで出来上がった文字列を、さらに②で結合していく。
リクエストメソッドはGET、FQDNはmb.api.cloud.nifty.com、クラスパスは/2013-09-01/classes/(クラス名)である。今度は\nで結合する。
$strconcat="GET\nmb.api.cloud.nifty.com\n/2013-09-01/classes/$classname\n$str"
これで署名文字列が出来上がったので、これをメッセージとし、クライアントキー(アプリケーションキーとは別)を秘密鍵としてSHA256でハッシュする。
$hash=hash_hmac("sha256",$strconcat,$clientkey,true);
hash_hmac()の第4引数にはtrueを指定しないと16進数表記の文字列が出力になってしまうので、忘れないように。
あとはBase64でエンコードすればよい。
$signature=base64_encode($hash);
これで晴れてシグネチャが完成したので、curlでリクエストを送信すればちゃんと結果が返ってくるはずである。
もしも"No such application"のエラーが返って来たら、多分シグネチャが間違っているので、クエリ ストリングに間違いがないか確認しよう。
以上である。これでセキュリティもばっちり(白目)。
phpはだいたい何でも関数が標準で用意されてて使えば使うほど便利に思えてくる。Javaはクソ。