SharePoint アドイン製品一覧
SharePoint 2010 開発のステップ・バイ・ステップ
Windows Azure 入門
Windows Azure How-To 集
WCF / WF 入門
環境 :Visual Studio 2010Windows Azure SDK 1.4(Windows Azure 利用方法は下記補足)
REST サービス / Web Api の実践
ここでは、応用的なテーマをとりあげます。基本的な構築手順については、「REST サービスの作成」 (WCF の場合)、または 「Getting Started with ASP.NET Web Api」 (ASP.NET の場合) を参照してください。(2012/02 変更 : 「WCF Web Api」は、今後、「ASP.NET Web API」としてリリース予定です。)
こんにちは。
勝手に連載化してしまい、すみません。REST サービスの構築について、現実の開発シナリオに即して、気づいた点を、適宜、記載していきたいと思います。(以前記載した内容も連載の一部に組み込みたいと思います。)
せっかくクラウドに配置するならクレーム ベースのほうがカッコ良いですが、今日は、REST サービスで Basic 認証をおこなう方法について記載します。もちろん、ただ Basic 認証をおこなうだけなら設定 1 つで可能ですが、サービスを公開する上では、いくつか考えなければならない組み合わせが出てきますので、改めて、記載してみたいと思います。
まず、「Basic 認証 (Basic Authentication) を使うと、Windows アカウント (Windows Identity) しか使えない」 と、まだ思われている方は、かなり昔に記述した こちら を参照してください。現実の公開される REST サービスで Windows アカウントは使わないと思いますが、Basic 認証では、Windows アカウント以外を使ったカスタマイズが可能です。(ただし、WCF を使用した場合です。)
補足 : なお、Web アプリケーション (ASP.NET) と共に使用する REST サービス (JavaScript などから呼び出すサービス)の場合は、ASP.NET MVC で JsonResult を使用したコントローラーを構築すれば OK です。(2012/03 追記 : ASP.NET MVC 4 以降では、ASP.NET Web API を使うことができます。)
こうしたカスタムの Basic 認証を使った REST サービスの公開をおこないたい場合、以下の 2 つの方法があります。
今回は、後者の方法で REST サービスを構築して、Windows Azure 上に配置してみましょう。このため、今回のサンプルでは、Windows アカウントを使用する代わりに、SQL Azure 上に配置したメンバーシップ (membership) データベースを使用します。
なお、ここではサンプル コードとして紹介していますが、日本の開発者の方 (坂本さん) が構築された ASP.NET HTTP Authentication Module を NuGet から取得して使用できます。ASP.NET HTTP Authentication Module は、Basic 認証だけでなく、Digest 認証にも対応しています。(2012/03 追記)
メンバーシップ (Membership) データベースの作成
まず、SQL Azure 上に ASP.NET のメンバーシップ プロバイダー用のデータベースを構築します。下記のスクリプトを使用します。(インストールに関する細かな留意点などは、こちら を参照してください。ASP.NET の多くのプロバイダーが SQL Azure を使って動かせますが、Session State プロバイダーは、SQL Azure では使用できませんので注意してください。)
[MSDN Archive] KB2006191 - Updated ASP.NET scripts for use with SQL Azure :http://archive.msdn.microsoft.com/KB2006191
今回は、認証を動かすだけなので、上記の InstallMembership.sql を使用します。SQL Client を使って、下記の通り、実行します。(下記で、AAAAAAAAAA には SQL Azure の名前空間を、testuser / XXXXXXX には SQL Azure へのログイン ID / パスワードを設定してください。)
rem -- aspnetdb のデータベースを作成sqlcmd -S AAAAAAAAAA.database.windows.net -U testuser@AAAAAAAAAA -P XXXXXXX -i InstallAzure.sqlrem -- aspnetdb に、共通スキーマをインストールsqlcmd -S AAAAAAAAAA.database.windows.net -d aspnetdb -U testuser@AAAAAAAAAA -P XXXXXXX -i InstallCommon.sqlrem -- aspnetdb に、メンバーシップ データベースをインストールsqlcmd -S AAAAAAAAAA.database.windows.net -d aspnetdb -U testuser@AAAAAAAAAA -P XXXXXXX -i InstallMembership.sql
補足 : SQL Azure への接続では、TCP が使用されます。企業のプロキシー設定などで、TCP をブロックしている場合は、上記の方法では接続できないので注意してください。(この場合、SQL Azure の管理ポータルを使用して、スクリプトを実行してください。)
カスタムの Http Module の作成
今回は、カスタム (Custom) の Http Module を、アプリケーションと同一のプロジェクト内に構築します。
まず、[Windows Azure Project] を新規作成します。(今回、ターゲット フレームワークとして .NET Framework 4 を使用します。)このあとの開発のため、ロールとして [WCF Services Web Role] を追加し、この Web Role のプロジェクトで [追加] - [新しい項目] を選択して、[クラス] を追加します。(今回、この名前を MyBasicAuthModule.cs とします。)
上記のリンクで紹介しているソースコード (昔のコード) と同様の方法で、下記の通り実装します。(ただし、上記のコードをそのままコピーしたんですが、ちゃんと動かなかったので、一部変更しました。ごめんなさい。。。)下記の通り、IHttpModule インタフェースを実装します。この IHttpModule では、Init と Dispose だけ実装すれば良く、Init 処理で、必要なイベント処理を追加して実装します。(なお、下記の ValidateCredentials 関数は、あとで作成します。)
. . .using System.Security.Principal;using System.Text;. . .public class MyBasicAuthModule : IHttpModule{ public void Init(HttpApplication context) { context.AuthenticateRequest += new EventHandler(this.AuthenticateUser); } public void Dispose() { } public void AuthenticateUser(Object source, EventArgs e) { HttpApplication application = (HttpApplication)source; HttpContext context = application.Context; String userName = null; String password = null; // memo : // This function is called for each request. So, it's better to use cache, etc ... (Here, I skipped this functionality.) // 1. Get username and password from authorizationHeader // 2. Validate username and password String authorizationHeader = context.Request.Headers["Authorization"]; if (!ExtractBasicCredentials(authorizationHeader, ref userName, ref password) || !ValidateCredentials(userName, password)) { // Failed : request basic auth using header context.Response.StatusCode = 401; context.Response.AddHeader("WWW-Authenticate", "Basic realm =\"demo\""); } else { // Succeeded : create and set user principal object context.User = new GenericPrincipal(new GenericIdentity(userName), null); } return; } // Get username and password from Http header (true = success, false = fail) protected bool ExtractBasicCredentials( String authorizationHeader, ref String username, ref String password) { const String HttpBasicSchemeName = "Basic"; if ((authorizationHeader == null) || (authorizationHeader.Equals(String.Empty))) return false; String verifiedAuthorizationHeader = authorizationHeader.Trim(); if (verifiedAuthorizationHeader.IndexOf(HttpBasicSchemeName) != 0) return false; // Get sub string (eliminated the first "Basic" string) from verifiedAuthorizationHeader verifiedAuthorizationHeader = verifiedAuthorizationHeader.Substring( HttpBasicSchemeName.Length, verifiedAuthorizationHeader.Length - HttpBasicSchemeName.Length).Trim(); // Decode the base64 encoded string byte[] credentialBase64DecodedArray = Convert.FromBase64String(verifiedAuthorizationHeader); UTF8Encoding encoding = new UTF8Encoding(); String decodedAuthorizationHeader = encoding.GetString(credentialBase64DecodedArray, 0, credentialBase64DecodedArray.Length); // Get username and password string int separatorPosition = decodedAuthorizationHeader.IndexOf(':'); if (separatorPosition <= 0) return false; username = decodedAuthorizationHeader.Substring(0, separatorPosition).Trim(); password = decodedAuthorizationHeader.Substring(separatorPosition + 1, (decodedAuthorizationHeader.Length - separatorPosition - 1)).Trim(); if (username.Equals(String.Empty) || password.Equals(String.Empty)) return false; return true; } // Validate username and password (true = Valid, false = Invalid) protected bool ValidateCredentials(String userName, String password) { // We'll create this later . . . }}. . .
上記では、毎回、Web ページが Request されるたびに、AuthenticateUser メソッドが呼び出されます。また、認証に成功した際に、単純に GenericPrincipal オブジェクトのみを設定していますが、ユーザーに関するその他の情報 (Principal) を設定する場合には、ここで設定をおこなってください。(ユーザー プロファイルなどを扱う場合には、上記で、メンバーシップ データベースだけでなく、プロファイル データベースなどもインストールしておいてください。)
さいごに、上記の ValidateCredentials メソッドを作成します。今回は、上記の ValidateCredentials メソッドで、SQL Azure 上に準備しておいたメンバーシップ (Membership) データベースを参照して認証の確認をおこないます。接続先のメンバーシップ プロバイダーについては、このあとで、Web.config 上に設定し、下記 (太字) のコードでは、この構成ファイル (.config) に設定されたプロバイダー情報を取得しています。(下記の通り、非常に簡単に取得できます。)
補足 : もちろん、ここで、独自なストア (クラウド上の別のデータベースなど) から情報を取得して、独自な認証処理を記述して頂いて構いません。
. . .using System.Web.Security;. . .// Validate username and password (true = Valid, false = Invalid)protected bool ValidateCredentials(String userName, String password){ // Get default provider // (if you get named provider, please use Membership.Provider["<Provider Name>"] ) MembershipProvider prov = Membership.Provider; return prov.ValidateUser(userName, password);}. . .
さいごに、Web Role のプロジェクトの Web.config を開き、下記太字の通り追記します。
ここでは、HTTP の要求の際に実行される上記のモジュール (MyBasicAuthModule クラス) の設定と、上記の ValidateCredentials メソッドで使用している「既定 (Default) のメンバーシップ プロバイダー」の設定をおこなっています。(下記で、AAAAAAAAAA には SQL Azure の名前空間を、testuser / XXXXXXX には SQL Azure へのログイン ID / パスワードを設定してください。)
<?xml version="1.0"?><configuration> <configSections> </configSections> . . . <connectionStrings> <add name="SqlConn" connectionString="Data Source=AAAAAAAAAA.database.windows.net;Initial Catalog=aspnetdb;User Id=testuser@AAAAAAAAAA;Password=XXXXXXX" providerName="System.Data.SqlClient"/> </connectionStrings> . . . <system.web> <compilation debug="true" targetFramework="4.0" /> <membership defaultProvider="SqlAzureMemProvider" userIsOnlineTimeWindow="15"> <providers> <clear /> <add name="SqlAzureMemProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="SqlConn" applicationName="MyRestService" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" passwordFormat="Hashed" /> </providers> </membership> </system.web> . . . <system.webServer> <modules runAllManagedModulesForAllRequests="true"> <add name="MyBasicAuthModule" type="WCFServiceWebRole1.MyBasicAuthModule" /> </modules> </system.webServer></configuration>
補足 : SqlMembershipProvider を使用したことがある方はご存じかと思いますが、上記 (Web.config) で、applicationName 属性は重要です。ユーザー認証の確認 (ValidateUser) では、ユーザー ID / パスワードだけでなく、applicationName も参照します。
アプリケーションの作成と配置
以上で、今回説明したい部分のほとんどは、完了です。
あとは、REST Service (Web Api) を構築します。ここでは、この構築方法は省略しますが、構築手順の詳細については、こちら (この中の「REST サービスの作成」) を参考にしてください。細かな制御が必要な場合は、まだ Preview 版ですが、WCF Web API (旧 WCF REST Starter Kit) を使用すると良いでしょう。また、ASP.NET Web Api を使用して構築することもできます。(2012/02 変更 : 「WCF Web Api」は、今後、「ASP.NET Web API」としてリリース予定です。)
補足 : この他に、ユーザー情報の登録・変更など、メンテナンス用の画面の開発なども必要になりますが、今回は、この構築については省略します。なお、上記の SQL Azure のメンバーシップ データベースは、上記の通り .config に設定して、ASP.NET の通常のメンバーシップを扱った開発でも使用できますので、ASP.NET の標準的な開発手順で こうした画面を構築できます。
あとは、いつもの通り、以下の手順で、上記のアプリケーションを Windows Azure に配置します。
実際の開発では、一度 ログインした情報などをキャッシュする仕組みなど (HttpContext.Current.Cache によるメモリ ヒープを使ったキャッシュ、など)、さまざまな処理を実装してください。(ここでは、必要最小限度の処理のみを構築しています。)また、この方法は、同様に、WCF Data Services でも使用できます。
Please see here using a browser comaptible with iframe . . .