予測: クラウド
クラウド サービスのマッシュアップ
Joseph Fultz
先月までは、Microsoft Windows Azure や SQL Azure を使用してソリューション アーキテクチャを補強することに時間を費やしてきました。今月は、複数のクラウド サービスを組み合わせて 1 つのアプリケーションにする方法を取り上げます。ここでは、Windows Azure、Windows Azure AppFabric アクセス コントロール、Bing Maps、および Facebook を組み合わせて、複数のクラウド サービスを構成する例を示します。
フェデレーション ID やソーシャル ネットワークの現実的価値について考えるとちょっとややこしそうだと感じる方のために、Marcelus 氏を紹介したいと思います。彼は私の友人で、地域密着型のクリーニング会社を経営しています。彼は私の父と同様、仕事上や個人的な取引において、たいていはある種のバーター方式で、他人に代わって必要なことを実行したり、希望する品物を入手したりしてくれる人物を知っています。このやり方を悪名高い田舎風の人脈ネットワークだと感じる方もいらっしゃるかもしれませんが、私は彼を見るたびに、強力なソーシャル ネットワークと Windows Azure AppFabric アクセス コントロール サービス (略称、ACS) を組み合わせたような人物だと感じています。実社会では、Marcelus のような友人がいれば、助っ人を紹介してもらえます。
しかし、仮想世界でさまざまなクラウド サービスを利用しようとすると、たいていはそのサービスの機能にアクセスする前に自分が誰であるかを知らせる必要があります。Marcelus をプログラミングして Web ページにサービスを提供させることはできないため、ここでは図 1 のようなクラウド サービスを使用して機能を提供することにします。
図 1 クラウド サービスとその機能
サービス | 機能 |
Windows Azure | サイトをホストし、ページにサービスを提供する |
AppFabric アクセス コントロール | サイトと Facebook との間の認証の管理とネゴシエーションを行う |
ユーザーを認証し、ソーシャル ネットワーク サービスを提供する | |
Bing Maps | 友人の出身地を表示する |
今回のシナリオでは、サイトのホーム ページにナビゲーションを行うと、Facebook で認証され、クレームがサイトに返されます。次に、サイトは Facebook からそのユーザーの友人を探し、選択した友人の情報を取得します。選択した友人が出身地を登録していれば、ユーザーが出身地名をクリックすると、Bing Map にその地域を表示します。
複数のサービス間の認証を構成する
MSDN マガジンの 2010 年 12 月号に、ACS の概要に関する優れた記事 (msdn.microsoft.com/magazine/gg490345) が掲載されています。今月は、サイトと Facebook のフェデレーションに必要な具体的処理について説明します。フェデレーションを正しく実現するために、ここでは Windows Azure AppFabric の開発者向けプレビュー版の AppFabric Labs を使用します。また、Windows Azure SDK 1.3 を使用し、Windows Identity Foundation SDK 4.0 もインストールしています。まず、portal.appfabriclabs.com (英語) にアクセスして登録しました。ACS にアクセスできるようになったら、CodePlex の「アクセス コントロール サービスのサンプルとドキュメント (ラボ)」(bit.ly/fuxkbl、英語) の冒頭部分の指示に従って、サービスの名前空間を設定します。次の目標は Facebook を ID プロバイダーとして設定することですが、そのためには Facebook アプリケーションを作成しておく必要があります (bit.ly/e9yE3I (英語) の指示を参照)。アプリケーションを作成すると、図 2 のような概要が表示されます。
図 2 Facebook アプリケーションの構成概要
この概要ページは重要です。Facebook を ACS の ID プロバイダーとして構成する際に、このページの情報を使用する必要があります。具体的には、ACS の構成情報に表示されている [Application ID] (アプリケーション ID) と [Application secret] (アプリケーション シークレット) の値が必要です (図 3 参照)。
図 3 ACS Facebook ID プロバイダーの構成
[Application permissions] (アプリケーションのアクセス許可) ボックスに friends_hometown を追加しています。これは友人の出身地の住所を地図に指示するために必要で、ここで指定しておかないと、既定では友人の出身地の情報が返されません。Graph API の呼び出しを使用してユーザーに関する他のデータを取得する場合は、Facebook 開発者サイト (bit.ly/c8UoAA、英語) を参照して、[Application permissions] (アプリケーションのアクセス許可) ボックスの一覧に追加しておく必要があります。
ACS を操作する際に知っておく必要があるのは、各 ID プロバイダーを使用する "証明書利用者" を指定することです。サイトの URL が jofultz.cloudapp.net であれば、ID プロバイダーの構成でこの URL を証明書利用者として指定します。これは、localhost にも当てはまります。そのため、アプリケーションのテスト時にはクラウドに配置したくなければ、localhost を証明書利用者として構成し、選択する必要があります (図 4 参照)。
図 4 ACS Facebook ID プロバイダーの構成: 証明書利用者
図 3 と図 4 の情報は、ID プロバイダーを構成する際、同じページに表示されます。同様に、localhost だけを許可するように構成したら、Web サイトから認証を受けようとしても失敗します。カスタム ログイン ページを作成することもできます。そのために、ACS の管理サイトの [Application Integration] (アプリケーションの統合) セクションに、ログイン ページの作成に関するガイドとサンプルが用意されています。今回のサンプルでは、ACS でホストされる既定のページをそのまま使用します。
ここまでは、ACS と Facebook アプリケーションを構成して、起動後に連携するようにしました。次は、この ID プロバイダーをサイトの認証手段として構成します。最も簡単な構成方法は、bit.ly/ew6K5z (英語) で公開されている Windows Identity Foundation SDK 4.0 をインストールすることです。SDK をインストールすると、右クリック メニューに [Add STS reference] (STS 参照の追加) というオプションが表示されるようになります (図 5 参照)。
図 5 [Add STS reference] (STS 参照の追加) メニュー オプション
このサンプルでは、Visual Studio で [新しい Web ロール プロジェクト] を選択して作成した既定の ASP.NET サイトを使用しています。作成したらそのサイトを右クリックし、ウィザードの指示に従って操作します。ここでは既存のセキュリティ トークン サービス (STS) を使用してサイトを構成します。そのためには、ウィザードでこの構成方法のオプションを選択し、WS-Federation メタデータのパスを指定します。アクセス コントロールの名前空間の場合、パスは次のようになります。
jofultz.accesscontrol.appfabriclabs.com/
FederationMetadata/2007-06/
FederationMetadata.xml
この情報を使用して、ウィザードは <microsoft.identityModel/> という構成セクションをサイトの構成に追加します。この構成セクションが追加されたら、<system.web/> 要素の下に <httpRuntime requestValidationMode="2.0" /> を追加します。localhost を証明書利用者として指定しておくと、アプリケーションを実行でき、起動時に、ACS でホストされるログイン ページが表示されます。このページには、Facebook を指定します (構成によっては、Windows Live や Google を指定することもできます)。microsoft.identityModel 要素は Microsoft.Identity アセンブリの有無に依存しているため、サイトでのその DLL への参照を必ず "常にコピーする" に設定する必要があります。このように設定しないと、Windows Azure への配置時に DLL がサイトに含まれず、サイトを実行できなくなります。先ほど述べたように、localhost と Windows Azure でホストされる両方のサイトの構成が必要になることから、ウィザードの完了後にもう一方を構成することになります。つまり、ウィザードで localhost のパスを構成したのであれば、次に Windows Azure サイトのパスを <audienceUris> 要素に追加する必要があります。
<microsoft.identityModel>
<service>
<audienceUris>
<add value="http://jofultz.cloudapp.net/" />
<add value="http://localhost:81/" />
</audienceUris>
また、構成ファイルに含まれる wsFederation 要素の realm 属性に、現在予定している実行場所を反映する必要があります。したがって、Windows Azure に配置するときの構成は次のようになります。
<federatedAuthentication>
<wsFederation passiveRedirectEnabled="true" issuer=
"https://jofultz.accesscontrol.appfabriclabs.com/v2/wsfederation"
realm="http://jofultz.cloudapp.net/" requireHttps="false" />
<cookieHandler requireSsl="false" />
</federatedAuthentication>
ただし、このサイトをデバッグする際、(ローカルでのデバッグのために) localhost でも正しく機能するようにするのであれば、ローカルでのサイトのホスト先を表すよう、たとえば次のように realm 属性を変更します。
<federatedAuthentication>
<wsFederation passiveRedirectEnabled="true"
issuer="https://jofultz.accesscontrol.
appfabriclabs.com/v2/wsfederation"
realm="http://localhost:81/"
requireHttps="false" />
<cookieHandler requireSsl="false" />
</federatedAuthentication>
すべて適切に構成すると、サイトを実行できるようになります。既定のページを参照しようとすると、ACS でホストされるログイン ページにリダイレクトされ、このページで Facebook を ID プロバイダーとして選択できます。Facebook を選択すると、認証のために Facebook のログイン ページに転送されます (図 6 参照)。
図 6 Facebook のログイン ページ
このアプリケーションを以前に使用したことがないため、Facebook からこのアプリケーションに対して [Request for Permission] (アプリケーションの許可) ダイアログ ボックスが表示されます (図 7 参照)。
図 7 アプリケーションのアクセス許可要求
このようなすばらしいアプリケーションを使用する集団にはぜひとも仲間入りするつもりなので、すぐに [Allow] (許可する) をクリックします。続いて、Facebook、ACS、およびアプリケーションの間で (ブラウザーのリダイレクトを使用して) 情報が交換され、最終的にはアプリケーションにリダイレクトされます。この時点では、空のページが表示されるだけですが、私の身元がアプリケーションに認識されているため、ページの右上に "Welcome Joseph Fultz" というメッセージが表示されます。
Facebook の Graph API
アプリケーションでは、私のソーシャル ネットワークのメンバーになっている友人を探して、その友人に関する情報を取得する必要があります。Facebook には、開発者がこのような処理を実行できるようにする Graph API が用意されています。Graph API はドキュメントが非常に充実しており、何よりも、実装が単純明快なので理解しやすく使いやすい API です。要求には、アクセス トークンが必要です。さいわい、アクセス トークンはクレーム内に返されており、Windows Identity Foundation SDK を利用しているため、クレームはプリンシパル ID に含まれています。クレームは次のようになっています。
https://schemas.xmlsoap.org/ws/2005/05/
identity/claims/nameidentifier
https://schemas.microsoft.com/ws/2008/06/
identity/claims/expiration
https://schemas.xmlsoap.org/ws/2005/05/
identity/claims/emailaddress
https://schemas.xmlsoap.org/ws/2005/05/
identity/claims/name
http://www.facebook.com/claims/AccessToken
https://schemas.microsoft.com/
accesscontrolservice/2010/07/claims/
identityprovider
ここで本当に取得する必要があるのは、クレーム全体のうち最後の部分 (nameidentifier、expiration など) とその関連値です。このため、次のような ParseClaims メソッドを作成して、クレームを分割し、クレームとその値をハッシュ テーブルに格納して後で使用できるようにします。このメソッドは、ページの読み込みイベントで呼び出します。
protected void ParseClaims()
{
string username = default(string);
username = Page.User.Identity.Name;
IClaimsPrincipal Principal = (IClaimsPrincipal) Thread.CurrentPrincipal;
IClaimsIdentity Identity = (IClaimsIdentity) Principal.Identity;
foreach (Claim claim in Identity.Claims)
{
string[] ParsedClaimType = claim.ClaimType.Split('/');
string ClaimKey = ParsedClaimType[ParsedClaimType.Length - 1];
_Claims.Add(ClaimKey, claim.Value);
}
}
FBHelper クラスを作成し、このクラスに、必要な Facebook の情報にアクセスするメソッドを作成します。まず、必要な要求をすべて実行できるようにするメソッドを作成します。次のように、WebClient オブジェクトを使用して各要求を作成し、JavaScript シリアライザーを使って応答を解析します。
public static Hashtable MakeFBRequest(string RequestUrl)
{
Hashtable ResponseValues = default(Hashtable);
WebClient WC = new WebClient();
Uri uri = new Uri(String.Format(RequestUrl, fbAccessToken));
string WCResponse = WC.DownloadString(uri);
JavaScriptSerializer JSS = new JavaScriptSerializer();
ResponseValues = JSS.Deserialize<Hashtable>(WCResponse);
return ResponseValues;
}
このコードからわかるように、各要求は、クレーム内にアクセス トークンが返されていることを必要とします。この再利用可能な要求メソッドを作成したら、友人を探し、各友人の Facebook ID と名前を含むハッシュ テーブルに解析するメソッドを作成します。
public static Hashtable GetFBFriends(string AccessToken)
{
Hashtable FinalListOfFriends = new Hashtable();
Hashtable FriendsResponse = MakeFBRequest(_fbFriendsListQuery, AccessToken);
object[] friends = (object[])FriendsResponse["data"];
for (int idx = 0; idx < friends.Length;idx++ )
{
Dictionary<string, object> FriendEntry =
(Dictionary<string, object>)friends[idx];
FinalListOfFriends.Add(FriendEntry["id"], FriendEntry["name"]);
}
return FinalListOfFriends;
}
友人一覧の応答をシリアル化解除すると、Hashtable、Hashtable、Dictionary の順に入れ子になった構造になります。このため、情報を取り出して独自のハッシュ テーブルに格納するためにちょっとした処理が必要です。情報をハッシュ テーブルに格納したら、default.aspx ページに移動し、ListBox を追加し、友人を取得してその結果をその ListBox にバインドします。
protected void GetFriends()
{
_Friends = FBHelper.GetFBFriends((string)_
Claims["AccessToken"]);
this.ListBox1.DataSource = _Friends;
ListBox1.DataTextField = "value";
ListBox1.DataValueField = "key";
ListBox1.DataBind();
}
この時点でアプリケーションを実行すると、認証後に Facebook の友人全員の一覧が表示されます。でもちょっと待ってください。まだ続きがあります。選択した友人のアクセス可能な情報を取得し、その情報を使用して地図上に友人の出身地を表示できるようにするんでした。先ほどの FBHelper クラスに戻って、アクセス トークンと選択した友人の ID を取得するメソッドを追加します。
public static Hashtable GetFBFriendInfo(string AccessToken, string ID)
{
Hashtable FriendInfo =
MakeFBRequest(String.Format(_fbFriendInfoQuery, ID) +
"?access_token={0}", AccessToken);
return FriendInfo;
}
どちらの Facebook のヘルパー メソッドでも、必要な Graph API クエリを含む定数文字列を参照していることに注意してください。
public const string _fbFriendsListQuery =
"https://graph.facebook.com/me/friends?access_token={0}";
public const string _fbFriendInfoQuery = "https://graph.facebook.com/{0}/";
最後の Facebook メソッドを作成したら、GridView をページに追加し、ハッシュ テーブルにバインドするよう設定します。続いて、分離コードに含まれる ListBox の SelectedIndexChanged メソッドで、GridView を GetFBFriendInfo メソッドから返された Hashtable 型の値にバインドします (図 8 参照)。
図 8 GridView の追加
protected void ListBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Debug.WriteLine(ListBox1.SelectedValue.ToString());
Hashtable FriendInfo =
FBHelper.GetFBFriendInfo((string)_Claims["AccessToken"],
ListBox1.SelectedValue.ToString());
GridView1.DataSource = FriendInfo;
GridView1.DataBind();
try
{
Dictionary<string, object> HometownDict =
(Dictionary<string, object>) FriendInfo["hometown"];
_Hometown = HometownDict["name"].ToString();
}
catch (Exception ex)
{
_Hometown = "";//Not Specified";
}
}
これで、Facebook から友人とその関連情報を取得したので、地図上に出身地を表示する処理に進みましょう。
やっぱり家が一番
このアプリケーションでは、出身地を登録している友人の出身地名をクリックすると、その地域に地図が移動します。まず、ページに地図を追加します。この作業は非常に簡単なうえ、Bing には地図を追加するための優れた対話型の SDK が用意されています。SDK では、機能を試してからソース コードを参照してコピーできます。この SDK は microsoft.com/maps/isdk/ajax/ (英語) にあります。default.aspx ページに、地図を保持するための div タグを追加します。
<div id="myMap" style="position:relative; width:400px; height:400px;" ></div>
ただし、そこに地図を取得するには、SiteMaster ページにスクリプト参照と簡単なコードを追加することになります。
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/
mapcontrol/mapcontrol.ashx?v=6.2"></script>
<script type="text/javascript">
var map = null;
function GetMap() {
map = new VEMap('myMap');
map.LoadMap();
}
</script>
このように変更すると、ページの取得時に既定の位置で地図が表示されます。しかし、ここでの目的は、友人の出身地を選択したらその地域に移動することです。前述の SelectedIndexChanged イベントでは、ページ内のラベルを名前にバインドし、クライアント側のクリック イベントを追加して、ラベルの値に基づいて地図で位置を特定します。
onclick="map.Find(null, hometown.innerText,
null, null, null, null, true, null, true);
map.SetZoomLevel(6);"
map.Find の呼び出しでは、指定する必要がなければ後半のパラメーターを省略できます。Find メソッドのリファレンスについては、msdn.microsoft.com/library/bb429645 を参照してください。この単純な例で地図を表示して操作するために必要な処理は、以上で完了です。これで、完全版のアプリケーションを実行できるようになりました。
前述のように localhost で機能するよう identityModel プロパティを適切に構成していると、F5 キーを押せば、ローカルのデバッグ モードでアプリケーションが実行されます。F5 キーを押すと、ブラウザー ウィンドウが開き、ログイン オプションが表示されます。Facebook を選択すると、図 6 のようなログイン ページが表示されます。ログインすると、default.aspx ページが再度表示されます。このページには、今度は図 9 のように友人と既定の地図が表示されます。
図 9 デモ ホーム ページ
次に、友人を参照して、その 1 人をクリックします。友人のセキュリティ設定と、ID プロバイダーの設定時に要求したアプリケーションのアクセス許可 (図 2 参照) に応じて、アクセス可能な情報が表示されます。さらに、地図の上にある出身地名をクリックすると、出身地の中心に地図が移動します (図 10 参照)。
図 10 Bing Maps に表示された出身地
最後に
Windows Azure Platform、Bing Maps、および Facebook の複数の要素を組み合わせる方法についてわかりやすく説明でき、この作業がどれほど簡単かをお伝えできていればさいわいです。今回は ACS を使用して、クラウド テクノロジを組み合わせてサンプル アプリケーションを作成できました。少し手を加えれば、必要に応じて利用できる独自の ID サービスを統合することも簡単です。この ID フェデレーションの長所は、Windows Azure を使用すると、他のベンダーや他のプラットフォームのサービス向けに開発して、そのサービスを組み込める点です。このようなフェデレーション サービスを使用しなければ、単一のプロバイダーとそのプロバイダーのサービスに選択肢が限定されたり、信頼性の低い統合方法を考えなければならなかったりします。Microsoft Windows Azure Platform は強力ですが、その能力の一端は、他のクラウド サービスと簡単に組み合わせられることにあります。
Joseph Fultz は、ダラスにある Microsoft Technology Center のアーキテクトで、企業ユーザーおよび ISV と連携して、ビジネスや市場の要求に応じてソフトウェア ソリューションを設計しプロトタイプを作成しています。Tech·Ed などのイベントや、同様の社内トレーニング イベントで講演を行っています。
この記事のレビューに協力してくれた技術スタッフの Steve Linehan に心より感謝いたします。