第4回 フィルタ属性による認証/キャッシュ/セキュリティ対策の実装:連載:ASP.NET MVC入門(2/5 ページ)
ASP.NET MVCでは、認証やキャッシュ、セキュリティなどの付加機能を、本体のロジックとは別に、属性としてアプリケーションに追加できる。
アプリケーションに認証機能を追加したい − 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
実に、これだけの記述でよい(Web.configによるアクセス管理の設定は不要である)。そのほか、認証に必要な設定やログイン・ページについても、まずはプロジェクト・テンプレートがデフォルトで提供しているので、このままで最低限の認証機能は動作するはずだ。
さっそく、Hello/Indexアクションにアクセスしてみよう。
上図のように、自動的にログイン・ページが表示されれば、認証機能は正しく動作している。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
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>
デフォルトでは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
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
ModelStateプロパティ(厳密には、ModelStateプロパティによって取得できるModelStateDictionaryオブジェクト)は、第1回で紹介したように、エラーの発生元とエラー・メッセージとを管理するためのプロパティである。もしもログイン・ページのエラー・メッセージをカスタマイズしたい場合には、リストの太字の部分を適宜置き換えればよい。
ここでは、すべてのチェックを終えた後、その結果をModelState.IsValidプロパティの戻り値でもって判定しているわけだ。IsValidプロパティは、ModelStateDictionaryオブジェクトの中にエラー情報が含まれているかどうかをブール値で返すものだ。
実際にご覧いただければ分かるように、AccountControllerクラスにはかなりの量のコードが記述されており、さまざまなメソッドやクラス/インターフェイスが絡み合って動いている。単に、上で紹介した部分のコードを記述すれば動作するというものではないので注意されたい。自分でログイン・アクションを定義する場合には、まずは、用意されたAccountControllerクラスを基にして、必要な部分だけを書き換えるようにするとよいだろう。
Copyright© Digital Advantage Corp. All Rights Reserved.