特集
次世代XML Webサービスを試す Part 1
[改訂版]

2.WSEを使ったセキュア XML Webサービスの実装

インフォテリア株式会社
吉松 史彰
2003/01/10

Page1 Page2 Page3 Page4 Page5 Page6

XML WebサービスのセキュリティとWSE

 さて、WSEを使って実際に新しいXML Webサービスを開発してみよう。.NET Framework 1.0には、XML Webサービスを実装するためのフレームワークが2つある。

  • ASP.NET
  • .NET Remoting(.NETリモート処理)

 ただし、この2つには明確なすみ分けがある。ひと言でいえば、ASP.NETは相互運用性を重視した環境であり、.NET RemotingはCLR上のオブジェクトの忠実なサポートを重視した環境であるということだ。ということは、相手が.NET Frameworkベースのシステムであることが確実でない限り、.NET Remotingは選択肢から外れることになる。誤解されないようにいっておくが、.NET RemotingはCLR同士の接続に関しては非常に優れた機能を提供する。XML Webサービスのノードの内部で.NET Remotingを避ける理由はないだろう。だが、XML Webサービスの最重要課題は相互運用性の確保だ。XML Webサービスは相互運用性なくしては成り立たない。この点ではASP.NETに一日の長がある。そのため、WSEにはASP.NETベースの実装しか含まれていない。

 そういうわけで、さっそくVisual Studio .NET 2002(以下、VS.NET)上でASP.NETのWebサービスのプロジェクトを開発してみよう。この手順はすでにさまざまな場所で解説されているので、Insider.NETフォーラムの定期的な読者ならばすでにおなじみのはずだ。ここでは、次のようなメソッドを持つXML Webサービスを1つ開発してみた。要するに、SQL Server 2000のNorthwindデータベースからデータを取ってきてそれを返すという、何の意味もないメソッドだ。

using System.Data.SqlClient;
using System.Web.Services.Protocols;
・・・(略)・・・
[WebMethod]
public string GetData() {
  string ret = "";
  SqlConnection cn = null;
  try {
    cn = new SqlConnection(String.Format(
      "server=.;uid={0};pwd={1};database=Northwind",
      "user1", "pass1"));
    SqlCommand cmd = new SqlCommand(
      "SELECT LastName FROM Employees Where EmployeeID=1", cn);
    cn.Open();
    ret = (string)cmd.ExecuteScalar();
  } catch (SqlException sqlex) {
    throw new SoapException(
      "Something bad happened because of you.",
      new System.Xml.XmlQualifiedName(
        "Fault.Nevertheless", "urn:IAmTheLaw"), sqlex);
  } finally {
    cn.Close();
  }
  return ret;
}
データベースから取得した値を返す簡単なWebサービスの実装
VS.NETで試す場合は、[新しいプロジェクト]として、[Visual C# プロジェクト]と、テンプレートとして[ASP.NET Webサービス]を選択し、上記のコードをペーストする(データベース接続のためのユーザー名とパスワード部分は個々に設定する必要あり)。

 テスト実行してみると、次のとおり「Davolio」というデータが返されるはずだ。

XML Webサービスの実行画面
  VS.NETからXML Webサービスを実行した場合のテスト・ページ
  GetDataのリンク部分をクリックすると、この画面に移動する。
  の画面で[起動]ボタンを押すと、XML Webサービスの実行結果がXMLデータとして表示される。

 さて、これをXML Webサービスとして外部に公開するわけだが、ここでSQL Serverに対する接続方法に注目してほしい。ここではユーザー名とパスワードをリテラル文字列で設定している。これが堅牢なソリューションではないことはいうまでもないだろう。そこで、ユーザーが入力したユーザー名とパスワードを使って検証を行いたい場合はどうしたらいいだろうか。

 すぐに思いつくのは、HTTPの基本認証やダイジェスト認証だろう。SSLと併用すればのぞき見対策も申し分ないように思われる。だが、XML Webサービスの観点からは、残念ながらそれもあまりよいソリューションではない。例えば上記のXML Webサービスで、GetDataメソッドには認証機能が必要だが、GetUselessDataというもう1つのメソッドには認証機能が必要ない場合はどうだろう。HTTPの認証機能はWebアプリケーション単位で実行されるため、XML Webサービス全体に対してセキュリティがかけられてしまう。そのため、柔軟な対応ができない。

 そこで登場するのがWS-Securityだ。WS-Securityは、アクセスするWebサーバやXML Webサービスではなく、送受信するメッセージに対してセキュリティ情報を付加する「メッセージレベル・セキュリティ」を実現する、SOAPの拡張仕様である。メッセージレベルで適用されるので、同じ[WebMethod]に対してあるときはセキュリティ情報を付加して、またあるときは付加しないでアクセスすることすら可能になる。WSEには、WS-Securityの実装が含まれている。そこで、WSEを使って、上記のGetDataメソッドを「セキュアXML Webサービス」に変身させてみよう。

WSEを使ったセキュアXML Webサービスの実装

 ここでは、WS-Security仕様に含まれる<UsernameToken>を使って、ユーザー名とパスワードをSOAPヘッダに含め、それをXML Webサービス側で認証する方法を実装してみたい。細かい技術的内容については次回以降に解説するとして、まずは動きを概観してみることにしよう。

 WSEを使ってWS-Securityを実装したXML Webサービスを作る手順は単純だ。XML Webサービスのプロジェクトを開いて、次の操作を順番に行えばよい。

Microsoft.Web.Services.dllへ参照を追加する

 VS.NETの「参照の追加」機能で追加すればよい。

パスワードの認証を行うためのクラスをXML Webサービスのプロジェクトに追加する

 XML Webサービスのプロジェクトに、PasswordProvider.csファイルを新しく追加し、PasswordProviderクラスを作成する。

namespace WSE1 {
  public class PasswordProvider
      : Microsoft.Web.Services.Security.IPasswordProvider {
    public PasswordProvider() {}

    public string GetPassword
        (Microsoft.Web.Services.Security.UsernameToken username) {
      if (username.Username.Equals("user1"))
        return "pass1";
      else
        return "";
    }
  }
}
パスワード認証を行うためのクラス
このクラスはMicrosoft.Web.Services.Security.IPasswordProviderインターフェイスを実装している必要がある。

 パスワードを提供するクラスは、Microsoft.Web.Services.Security.IPasswordProviderインターフェイスを実装していなければならない。このインターフェイスにはGetPasswordというメソッドが1つだけ定義されている。内容は、user1というユーザー名に対して、「pass1」が正しいパスワードであるということを返答するというコードになっている。なお、WSE1というのがXML Webサービス・プロジェクトの名前であり、デフォルトの名前空間であると仮定している。

Web.configファイルを編集して、configuration要素の直下に次の要素を追加する

 これによって、上記で作成した正しいパスワードを提供するクラスが処理に取り込まれる。

<configSections>
  <section name="microsoft.web.services" type="Microsoft.Web.Services.Configuration.WebServicesConfiguration, Microsoft.Web.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</configSections>

<microsoft.web.services>
  <security>
    <passwordProvider type="WSE1.PasswordProvider, WSE1" />
  </security>
</microsoft.web.services>
作成したパスワード認証クラスを取り込むための設定
XML Webサービスのプロジェクトに含まれているWeb.configファイルを編集して追加する。

Web.configファイルを編集して、XML Webサービスの実行時にSoapExtensionが起動されるようにする

 具体的には、前述の(WSEのインストール・フォルダにある)WSE.configファイルの内容から、次の部分をコピーしてXML Webサービスのプロジェクトに含まれるWeb.configファイルのconfiguration/system.web要素の子要素として追加すればよい。

<webServices>
  <soapExtensionTypes>
    <add type="Microsoft.Web.Services.WebServicesExtension, Microsoft.Web.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" priority="1" group="0"/>
  </soapExtensionTypes>
</webServices>
Microsoft.Web.Services.WebServicesExtensionクラスの設定
このエントリもXML Webサービスのプロジェクトに含まれているWeb.configファイルに、configuration/system.web要素の子要素として追加する。

XML Webサービスのメソッドのコードを変更して、SOAPメッセージとともに渡されたユーザー名とパスワードを利用してSQL Serverにログインするようにする

 以下のリストが、先ほど作成したXML Webサービスのコードを変更したものだ(太字部分が変更・追加点)。

[WebMethod]
public string GetData() {
  Microsoft.Web.Services.SoapContext ctx =
    Microsoft.Web.Services.HttpSoapContext.RequestContext;
  Microsoft.Web.Services.Security.UsernameToken
                                          user = null;
  foreach(Microsoft.Web.Services.Security.SecurityToken
      token in ctx.Security.Tokens) {
    user = token as
            Microsoft.Web.Services.Security.UsernameToken;
    if (user != null)
      break;
  }
  string username = "", password = "";
  if (user != null) {
    username = user.Username;
    password = user.Password;
  } else {
    throw new SoapException(
      "Something bad happened because of you.",
      new System.Xml.XmlQualifiedName(
        "Fault.Nevertheless", "urn:IAmTheLaw"));
  }

  string ret = "";
  SqlConnection cn = null;
  try {
    cn = new SqlConnection(String.Format(
      "server=.;uid={0};pwd={1};database=Northwind",
      username, password));
    SqlCommand cmd = new SqlCommand(
      "SELECT LastName FROM Employees Where EmployeeID=1", cn);
    cn.Open();
    ret = (string)cmd.ExecuteScalar();
  } catch (SqlException sqlex) {
    throw new SoapException(
      "Something bad happened because of you.",
      new System.Xml.XmlQualifiedName(
        "Fault.Nevertheless", "urn:IAmTheLaw"), sqlex);
  } finally {
    cn.Close();
  }
  return ret;
}
変更後のXML WebサービスのGetDataメソッド
SOAPメッセージとともに渡されたユーザー名とパスワードを取り出す処理を追加する(太字が変更・追加部分)。

 これで開発は完了だ。WS-Securityをサポートするためのコードは、それほどの量ではないことが分かるだろう。


 INDEX
  [特集]次世代XML Webサービスを試す Part 1
    1.WSEのインストール
  2.WSEを使ったセキュア XML Webサービスの実装
    3.セキュアXML Webサービスのクライアントの実装
    4.ほかのWS-Security実装との相互運用(1)
    5.ほかのWS-Security実装との相互運用(2)
 
 「特集:次世代XML Webサービスを試す」



Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間