Hatena::Groupmediaforcelabs

御手洗の開発記録

 | 

2010-05-27

Silverlightのフォーム認証について(Active Directoryとの認証)

| 15:15

今日は、Silverlightでフォーム認証を実装する方法をまとめておきます。

システム構成

まずは、システムの構成から説明します。

Silverlight自体はブラウザ上で動作するクライアントアプリケーションなので、利用者情報を取得する際は、サーバ側にリクエストしてデータを取得します。

その際、WCFを利用します。

WCFとは、Windows Communication Foundationの略で、簡単にいうとWebサービスです。

今回は利用者情報などの個人情報を扱うので認証が必要になります。

認証は当然、独自の実装をすることもできるのですが、今回はフォーム認証を利用することにしました。

WCFの認証サービスについては以下の通りです。

サービスを使用するには、認証サービスにユーザーの資格情報を渡します。認証サービスでは、ASP.NET メンバーシップを使用して渡された資格情報の妥当性を検査します。


既定では、認証サービスは、ユーザー名およびパスワードを既定のメンバーシップ プロバイダーに渡すことで、妥当性を検査します。ユーザーが認証されると、ASP.NET 認証サービスは、ASP.NET フォーム認証と互換性がある HTTP Cookie として認証チケットを発行します。


以降の要求では、毎回ユーザーが資格情報を入力しなくてもよいように、このチケットが Web アプリケーションに渡されます。

参考URLhttp://msdn.microsoft.com/ja-jp/library/bb386582.aspx

図に表すとこんな感じです。

f:id:mitarai2009:20100527151436p:image

このような処理が設定だけで実現することができます。

サーバ側の設定

今回は、

サーバ側の設定は以下の通りです。

基本は、MSDNを参考にして書いています。

参考URLhttp://msdn.microsoft.com/ja-jp/library/dd560704(v=VS.95).aspx

1.Silverlight対応のWCFサービスを作成します。

ソリューション エクスプローラーで、サービス プロジェクトを右クリックし、[追加]、[新しい項目] の順にクリックして、[Silverlight] カテゴリから [Silverlight 対応の WCF サービス] テンプレートを選択します。

その際、[名前] ボックスに「Authentication.svc」と入力し、[追加] ボタンをクリックします。(サービス名はなんでもいいと思うが、今回はマニュアルに従いました)

2.Authentication.svc.cs ファイルを削除します。
3.Authentication.svc の内容を次のコードに置き換えます。
<%@ ServiceHost Language="C#" Debug="true" Service="System.Web.ApplicationServices.AuthenticationService" %>
4.Web.ConfigファイルにauthenticationService要素を追加し、認証サービスを有効化します。

以下の設定をWeb.Configファイルに追加してください。(configuration要素の子要素として追加してください)

<system.web.extensions>
  <scripting>
    <webServices>
      <authenticationService enabled="true" requireSSL="false" />
    </webServices>
  </scripting>
</system.web.extensions>

まだ、開発途中なので、requireSSL属性がfalseになってますが、リリース時にはtrueに変更する必要があります。

5.Web.config ファイルに記述されているservice要素の設定を変更します。

この要素は、1の時点でVisualStudioが自動で追加してくれています。

そのため、service属性のname属性の値と、service属性の子要素であるendpoint要素のcontract属性の値をSystem.Web.ApplicationServices.AuthenticationServiceに変更してください。

<service name="System.Web.ApplicationServices.AuthenticationService">
  <endpoint address="" binding="customBinding"
    bindingConfiguration="WorkManagementApplication.Web.Services.Authentication.customBinding0"
    contract="System.Web.ApplicationServices.AuthenticationService" />
  <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
6.フォーム認証を有効化します。

system.web要素の子要素としてauthentication要素を追加します。

その際、mode属性はFormsとします。

また、authentication要素の子要素としてforms要素を追加します。

その際、cookielessを追加し、値はUseCookiesを設定してください。

<authentication mode="Forms">
  <forms cookieless="UseCookies" />
</authentication>
7.メンバシッププロバイタをWeb.Configに設定します。

System.Web.ApplicationServices.AuthenticationServiceは認証の際にメンバシッププロバイダを利用して認証や資格情報の作成を行っています。

今回は、Active Directoryを使用して認証を実施するため、メンバシッププロバイダにはSystem.Web.Security.ActiveDirectoryMembershipProviderを使用します。

まずは、Active Directoryの接続情報を追加します。

<connectionStrings>
  <add name="ADConnectionString" connectionString="LDAP://%サーバアドレス%/%BaseDN%" />
</connectionStrings>

connectionStrings要素はconfiguration要素の子要素として追加します。

%サーバアドレス%にはActive Directoryサーバ名(IPアドレス)を設定し、%BaseDN%には利用者情報が格納されているディレクトリのDNを設定してください。

次に、メンバシッププロバイダをWeb.Configに追加します。

<membership defaultProvider="MembershipADProvider">
  <providers>
    <add name="MembershipADProvider"
      type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
      connectionStringName="ADConnectionString"
      connectionUsername="%ユーザ名%"
      connectionPassword="%パスワード%"
      attributeMapUsername="sAMAccountName" />
  </providers>
</membership>

membership要素はsystem.web要素の子要素として追加します。

connectionStringNameには、addのname属性で指定した文字列を設定してください。

%ユーザ名%と%パスワード%はActive Directoryの管理者ユーザを設定してください。

そして、attributeMapUsername属性は必ず指定してください。

この属性は、ユーザ名がどの属性に設定されているかどうか指定する属性となります。

この属性を設定しないと認証に成功しません。

このおかげで、結構はまりました。

ホント、MSDNは一言足りないと思います!!!

ちなみに、値はsAMAccountNameを設定しておけばまず間違いありません。

8.サービスのアクセス権限を設定します。

制限をかけたいサービスのアクセス権限を設定します。

今回は、指定したディレクトリ配下のサービスに匿名ユーザからのアクセスを禁止する設定を行いました。

方法は以下の通りです。

サービスを専用のディレクトリ ("secure/MyService.svc" など) に置き、そのディレクトリの構成ファイル ("secure/Web.config" など) を以下のような設定にします。

<configuration>
    <system.web>
      <authorization>
        <deny users="?" />
      </authorization>
    </system.web>
</configuration>

ちなみに、この設定を行うと、Silverlightプロジェクト側でサービス参照の追加や更新ができなくなります。

そのため、開発時にはまずサービスを作成し、インタフェースが定まったらWeb.Configを追加するという流れにしないめんどうです。

一応、authorizationの設定をコメントアウトにして更新して、コメントを元に戻すというやり方でサービス参照の更新は可能ですが、めんどくさいですよね。

なんかいい方法があるのかもしれませんが、それほど優先順位は高くないのでこのまま突き進みます。

以上でサーバ側の設定が完了です。

クライアント側(Silverlight)の設定

クライアント側は特に特別なことをする必要はありません。

認証サービスと制限のかかったサービスをサービス参照に追加し、サービスの実行を実装します。

サンプルとしては以下の通りです。

private void loginButton_Click(object sender, RoutedEventArgs e)
{
    // ログイン情報を取得する
    LoginPageContext loginPageContext = (LoginPageContext) Resources["loginPageContext"];

    // 認証処理を行う
    var authProxy = new AuthenticationServiceClient();
    authProxy.LoginCompleted += new EventHandler<LoginCompletedEventArgs>(authProxy_LoginCompleted);
    authProxy.LoginAsync(loginPageContext.UserId, loginPageContext.UserPassword, null, false);
}

private void authProxy_LoginCompleted(object sender, LoginCompletedEventArgs e)
{
    if (e.Error != null) throw e.Error; // TODO とりあえずそのまま返却する

    // ログイン結果を判定する
    if (e.Result)
    {
        // ログイン成功
        var userProxy = new UserAccessServiceClient();
        userProxy.GetUserDataCompleted += new EventHandler<GetUserDataCompletedEventArgs>(userProxy_GetUserDataCompleted);
        userProxy.GetUserDataAsync();
    }
    else
    {
        // TODO ログイン失敗
        ChildWindow cw = new ChildWindow();
        cw.Width = 400;
        cw.Height = 100;
        cw.Content = "ログインに失敗しました。";
        cw.Show();
    }
}

private void userProxy_GetUserDataCompleted(object sender, GetUserDataCompletedEventArgs e)
{
    // ユーザ情報を取得する
    UserContainer userContainer = e.Result as UserContainer;

    // ユーザ情報を保持する
    LoginUserContext userContext = (LoginUserContext)Application.Current.Resources["loginUserContext"];
    userContext.LoginDate = DateTime.Now;
    userContext.UserName = userContainer.lastNamek__BackingField + userContainer.firstNamek__BackingField;
    userContext.userContainer = userContainer;

    // 基底画面へ遷移する
    NavigationService.Navigate(new Uri("/Base", UriKind.Relative));
}

めんどくさいので説明は省略します。

以上で、終了です。

ThomasThomas 2012/02/18 04:01 I went to tons of links bferoe this, what was I thinking?

rmoamyljzgdrmoamyljzgd 2012/02/19 17:33 CBLouT <a href="http://mcdwkixxzhoe.com/">mcdwkixxzhoe</a>

hwapkecbkjhwapkecbkj 2012/02/19 23:49 CDGcQo , [url=http://urwxdpeyhwgr.com/]urwxdpeyhwgr[/url], [link=http://senmheftsdll.com/]senmheftsdll[/link], http://bmcegdmdhnth.com/

ゲスト



トラックバック - http://mediaforcelabs.g.hatena.ne.jp/mitarai2009/20100527
 |