連載
» 2009年08月14日 00時00分 公開

第4回 フィルタ属性による認証/キャッシュ/セキュリティ対策の実装連載:ASP.NET MVC入門(2/5 ページ)

[山田祥寛(http://www.wings.msn.to/),著]

アプリケーションに認証機能を追加したい − Authorize属性 −

 Authorize属性を利用することで、特定のアクション、またはコントローラに対して、認証やロール/ユーザーに基づくアクセス制御を有効化できる。

 なお、Authorize属性を利用する場合には、ASP.NET Webアプリケーション管理ツール(以降、管理ツール)からあらかじめ認証に使用するユーザーやロールを用意しておく必要がある。この方法については、別稿「.NET TIPS:セキュリティ・コントロールでログイン機能を作成するには?」でも解説しているので、併せて参照いただきたい。ここでは取りあえず、

  • Adminロールに属するyyamadaユーザー
  • ロールに属さないnkakeyaユーザー

がすでに用意されているものとして、話を進めよう。

 ここでは、第1回で作成したHello/Indexアクションに対して、Adminロールのユーザーにしかアクセスできないように制限を課すものとする。コードは次のようになる。

[Authorize(Roles="Admin")]
public ActionResult Index() {
  ViewData["msg"] = "こんにちは、ASP.NET MVC!";
  return View();
}

<Authorize(Roles:="Admin")> _
Function Index() As ActionResult
  ViewData("msg") = "こんにちは、ASP.NET MVC!"
  Return View()
End Function

リスト3 Hello/IndexアクションにAdminロールしかアクセスできないように制限を課したコード(上がHelloController.cs、下がHelloController.vb)

 実に、これだけの記述でよい(Web.configによるアクセス管理の設定は不要である)。そのほか、認証に必要な設定やログイン・ページについても、まずはプロジェクト・テンプレートがデフォルトで提供しているので、このままで最低限の認証機能は動作するはずだ。

 さっそく、Hello/Indexアクションにアクセスしてみよう。

図2 ログイン・ページに自動的にリダイレクトされる

 上図のように、自動的にログイン・ページが表示されれば、認証機能は正しく動作している。Adminロールに属するyyamadaユーザーでログインした場合にはHello/Index.aspxの内容が正しく表示されること、Adminロールを持たないnkakeyaユーザーでは、ログインに成功してもHello/Indexアクションにはアクセス「できない」ことを確認してほしい。

■Authorize属性で利用可能なプロパティ

 Authorize属性で利用可能なプロパティは、Roles、Usersプロパティだ。先ほどの例でも見たように、特定のロールにのみアクセスを認める場合にはRolesプロパティを使用する。

 ユーザー管理の視点からはあまり好ましくはないが、もしユーザー単位でアクセスを認めたいという場合には、代わりにUsersプロパティを使用すればよい。

 以下は、yyamadaおよびnkakeyaユーザーにアクセスを認める場合の記述である。

[Authorize(Users="yyamada, nkakeya")]
public ActionResult Index() {

  ……中略……

}

<Authorize(Users:="yyamada, nkakeya")> _
Function Index() As ActionResult

  ……中略……

End Function

リスト4 yyamada、nkakeyaユーザーにアクセスを認めるためのコード(上:
HelloController.cs、下:HelloController.vb)

 複数のユーザー/ロールを指定したい場合には、このリスト4のようにUsers/Roles属性に対して、カンマ区切りでユーザー名/ロール名を列挙すればよい。

 さらに、特定のロール/ユーザーではなく、ログイン済みのユーザーであれば無条件にアクセスを認めたいという場合もあるだろう。その場合には、Roles/Users属性いずれも指定せずに、ただ単に「[Authorize()]」や「<Authorize()> _」のように記述すればよい。

[参考]デフォルトで用意されたコードを確認する

 前述したように、ログイン・ページの設定やログインに関連するアクション・メソッド、ビュー・スクリプトは、プロジェクト作成時にデフォルトで用意されている。Authorize属性の設定だけで、そのほかはログインにかかわるコードを記述する必要がなかったのもそのためだ。

 もっとも、実際のアプリケーションでは、デフォルトの設定をそのまま使えるケースはまれだ。ログイン・ページをカスタマイズしたい、そもそもログイン・ページのパスを変更したいなどということもあるだろう。ここでは、デフォルトで用意されたコードを確認することで、自分でログイン・ページをカスタマイズするための参考にしていただきたい。

(1)ログイン・ページのパスを変更したい

 ログイン・ページのパスを設定しているのは、Web.configの<authentication>要素だ(この設定は、従来のASP.NETと同じだ)。

<authentication mode="Forms">
  <forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>

リスト5 ログイン・ページのパスを設定するコード(Web.config)

 デフォルトではAccount/LogOnアクションを呼び出す設定になっているので、この部分を適宜変更すればよい。

(2)ログイン・ページの挙動を変更したい

 ログイン/ログアウトに関連しているのは、AccountコントローラのLogOnメソッド(引数ありとなし)、LogOffメソッドの3つである。

 引数なしのLogOnメソッドがログイン・ページを表示するための、引数ありのLogOnメソッドがログイン処理を行うための、そして、LogOffメソッドが[Log Off]リンク*2がクリックされた場合に呼び出される、それぞれアクション・メソッドである。

*2 [Log Off]リンクは、デフォルトのマスター・ページ右上に、ログイン状態のときにのみ表示される。


 では、デフォルトで用意されたコードを見てみよう(コメントは筆者が記したもの)。

// ログイン・ページを呼び出すためのアクション
public ActionResult LogOn() {
  return View();
}

// ログイン・ページで[Log On]ボタンがクリックされたときに
// 呼び出されるアクション
[AcceptVerbs(HttpVerbs.Post)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
Justification = "Needs to take same parameter type as Controller.Redirect()")]
public ActionResult LogOn(string userName, string password, bool rememberMe, string returnUrl) {

  // ユーザー名/パスワードが妥当でない場合、
  // ログイン・ページを再描画
  if (!ValidateLogOn(userName, password)) {
    return View();
  }


  // 指定されたユーザー名でログイン(引数rememberMeは
  // ブラウザを閉じた後もログイン状態を残すかどうか)
  FormsAuth.SignIn(userName, rememberMe);

  // 引数returnUrl(もともとアクセスしたURL)が空でない場合、
  // もともと要求されたページにリダイレクト
  if (!String.IsNullOrEmpty(returnUrl)) {
    return Redirect(returnUrl);
  } else {

    // 引数returnUrlが空である場合は
    // Home/Indexアクションにリダイレクト
    return RedirectToAction("Index", "Home");
  }
}

// [Log Off]リンクをクリックしたときに呼び出されるアクション
public ActionResult LogOff() {
  // ログアウト処理の後、Home/Indexアクションにリダイレクト
  FormsAuth.SignOut();
  return RedirectToAction("Index", "Home");
}

' ログイン・ページを呼び出すためのアクション
Function LogOn() As ActionResult
  ViewData("Title") = "Log On"
  Return View()
End Function

' ログイン・ページで[Log On]ボタンがクリックされたときに
' 呼び出されるアクション
<AcceptVerbs(HttpVerbs.Post)> _
<System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", _
Justification:="Needs to take same parameter type as Controller.Redirect()")> _
Function LogOn(ByVal userName As String, ByVal password As String, ByVal rememberMe As Boolean, ByVal returnUrl As String) As ActionResult
  ViewData("Title") = "Log On"

  ' ユーザー名/パスワードが妥当でない場合、
  ' ログイン・ページを再描画
  If Not ValidateLogOn(userName, password) Then
    Return View()
  End If


  ' 指定されたユーザー名でログイン(引数rememberMeは
  ' ブラウザを閉じた後もログイン状態を残すかどうか)
  FormsAuth.SignIn(userName, rememberMe)

  ' 引数returnUrl(もともとアクセスしたURL)が空でない場合、
  ' もともと要求されたページにリダイレクト
  If Not String.IsNullOrEmpty(returnUrl) Then
    Return Redirect(returnUrl)
  Else

  ' 引数returnUrlが空である場合は
  ' Home/Indexアクションにリダイレクト
    Return RedirectToAction("Index", "Home")
  End If
End Function

' [Log Off]リンクをクリックしたときに呼び出されるアクション
Function LogOff() As ActionResult
  ' ログアウト処理の後、Home/Indexアクションにリダイレクト
  FormsAuth.SignOut()
  Return RedirectToAction("Index", "Home")
End Function

リスト6 ログイン/ログアウト時に実行されるコード(上がAccountController.cs、下がAccountController.vb)

 ValidateLogOnメソッド(太字部分)は、AccountControllerクラスの中で用意されたメソッドで、与えられたユーザー名とパスワードの妥当性を検証するためのメソッドである(リスト7)。それぞれユーザー名/パスワードが空でないこと、ユーザー名/パスワードの組み合わせが正しいかどうかを確認している。

private bool ValidateLogOn(string userName, string password) {

  // ユーザー名が空であるかどうかをチェック
  if (String.IsNullOrEmpty(userName)) {
    ModelState.AddModelError("username", "You must specify a username.");
  }

  // パスワードが空であるかどうかをチェック
  if (String.IsNullOrEmpty(password)) {
    ModelState.AddModelError("password", "You must specify a password.");
  }

  // ユーザー名/パスワードの組み合わせが正しいかどうかをチェック
  if (!MembershipService.ValidateUser(userName, password)) {
    ModelState.AddModelError("_FORM", "The username or password provided is incorrect.");
  }

  // エラーの有無を戻り値として返す
  return ModelState.IsValid;
}

Private Function ValidateLogOn(ByVal userName As String, ByVal password As String) As Boolean
  ' ユーザー名が空であるかどうかをチェック
  If String.IsNullOrEmpty(userName) Then
    ModelState.AddModelError("username", "You must specify a username.")
  End If

  ' パスワードが空であるかどうかをチェック
  If String.IsNullOrEmpty(password) Then
    ModelState.AddModelError("password", "You must specify a password.")
  End If

  ' ユーザー名/パスワードの組み合わせが正しいかどうかをチェック
  If Not MembershipService.ValidateUser(userName, password) Then
    ModelState.AddModelError("_FORM", "The username or password provided is incorrect.")
  End If
  ' エラーの有無を戻り値として返す
  Return ModelState.IsValid
End Function

リスト7 ユーザー名/パスワードの妥当性をチェックするコード(上がAccountController.cs、下がAccountController.vb)

 ModelStateプロパティ(厳密には、ModelStateプロパティによって取得できるModelStateDictionaryオブジェクト)は、第1回で紹介したように、エラーの発生元とエラー・メッセージとを管理するためのプロパティである。もしもログイン・ページのエラー・メッセージをカスタマイズしたい場合には、リストの太字の部分を適宜置き換えればよい。

 ここでは、すべてのチェックを終えた後、その結果をModelState.IsValidプロパティの戻り値でもって判定しているわけだ。IsValidプロパティは、ModelStateDictionaryオブジェクトの中にエラー情報が含まれているかどうかをブール値で返すものだ。

 実際にご覧いただければ分かるように、AccountControllerクラスにはかなりの量のコードが記述されており、さまざまなメソッドやクラス/インターフェイスが絡み合って動いている。単に、上で紹介した部分のコードを記述すれば動作するというものではないので注意されたい。自分でログイン・アクションを定義する場合には、まずは、用意されたAccountControllerクラスを基にして、必要な部分だけを書き換えるようにするとよいだろう。

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。