"技術的にできる" という実践例として MVC 4 + WebAPI という組み合わせで、 RESTful like なフォーム認証を作る方法を記載します。 MVC だけで実装する フォーム認証 と比べ、実装量が増える デメリット がありますが、操作感が良くなる(リクエスト時に固まるをなくせる)メリットはあると思います。
目次
サンプルコード
概要
ちょっと強引かもしれませんが、MVC4 で画面表示のみ行い、ログイン と ログアウト を ASP.NET WebAPI で行う実装をしてみます。 完成時の挙動を下記のシーケンス図に示します。
- サイトへアクセス
- 未認証なので、ログインページ へ リダイレクト
- ユーザーID、パスワード を Ajax リクエスト
- 認証結果の返信し、認証済みのクッキーを設定
- もともとアクセスしようとしたページへ リダイレクト
上記について、具体的には次のような実装を行います。 2 のリダイレクトは web.config で実現します。 3 のログイン処理は jQuery を使って、 Ajax リクエスト を行います。 5 のリダイレクトは JavaScript で実現します。
以下では、それぞれについて詳細を掲載します。
作成する ファイル 、 フォルダ 構成
MVC4 + WebAPI で フォーム認証 を行おうとしたとき、以下のようなファイルを作っていきます。 今回は、それなりにコード記述が必要ですが…ブログ中では重要部分のみで、詳細は割愛します。
-
/web.config
認証と承認の設定を記述します。 -
/Controllers/AuthenticationController.cs
認証の実処理を行います。 -
/Controllers/HomeController.cs
秘匿するページ。 -
/Controllers/UserController.cs
ログインページ。
web.config の認証設定
Authentication (認証) は フォーム認証 で設定します。 Authorization (承認) は ホワイトリスト方式 (許可するものだけを記載する方式) で設定したいので、 ルートですべて拒否し、認証処理に必要な部分だけ許可するように設定します。
web.config (一部抜粋)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | < configuration > < system.web > < authentication mode = "Forms" > < forms loginUrl = "/User/Login" ></ forms > </ authentication > < authorization > < deny users = "?" /> </ authorization > </ system.web > < location path = "Scripts" > < system.web > < authorization > < allow users = "*" /> </ authorization > </ system.web > </ location > < location path = "api/authentication" > < system.web > < authorization > < allow users = "*" /> </ authorization > </ system.web > </ location > </ configuration > |
ログイン、ログアウト 処理
ログイン、ログアウトは WebAPI で実装します。
以下には WebAPI の実装を掲載します。
やっていることは、受け取ったID、パスワードが正しいか判定して、正しければクッキーを発行する処理です。
認証済みであることを示すクッキーの発行、削除には FormsAuthentication クラス
を利用します。
AuthenticationController.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | namespace MvcApplication.Controllers { using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using System.Web.Security; using Models; public class AuthenticationController : ApiController { // // POST: /api/authentication/signin [HttpPost] public HttpResponseMessage SignIn(UserModel model) { if (model != null && model.Id == "hoge" && model.Password == "hoge" ) { FormsAuthentication.SetAuthCookie(model.Id, model.RememberMe); return this .Request.CreateResponse(HttpStatusCode.OK, new LoginResultModel() { IsSuccess = true , Message = "ログインに成功しました。" }); } else { return this .Request.CreateResponse(HttpStatusCode.Accepted, new LoginResultModel() { IsSuccess = false , Message = "ID または パスワード が違います。" }); } } // // POST: /api/authentication/signout [HttpPost] public HttpResponseMessage SignOut() { FormsAuthentication.SignOut(); return this .Request.CreateResponse(HttpStatusCode.OK); } } } |
ログイン 処理への Ajaxリクエスト
ログイン、ログアウト処理は POST で受け付けるように構成したので、 jQuery でも POST で ログイン の リクエスト を行います。 ログイン リクエスト を行うと同時に、画面をリクエスト中である表示に切り替えます。 ここでは、複数回のリクエストを行わせないようにするため、 input要素 を無効化(非活性)にする処理のみ入れています。 画面上に「読み込み中」表示など追加で行っても良いと思います。
index.js (一部抜粋)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /** * submit ボタン 押下時に呼び出されます。 * @param {jQuery.Event} event jQuery イベントオブジェクト */ var submitbutton_onclick = function (event) { // ログイン リクエスト $.ajax({ type: 'POST' , url: '/api/authentication/signin' , data: { id: $( '#id' ).val(), password: $( '#password' ).val(), rememberMe: $( '#rememberMe' ).prop( 'checked' ) }, success: ajax_onsuccess, error: ajax_onerror }); // input要素 を無効化 $( 'input' ).attr( 'disabled' , 'disabled' ); }; |
ログイン 後の リダイレクト
ログイン リクエスト が正常に返ってきた場合、所定の場所へ リダイレクト を行います。
リダイレクト先 は、クエリパラメター の ReturnUrl
に記載があるので、その値の場所へ window.location
を移動させます。
※ クエリ文字列の分解は こちら に記載しました。
index.js (一部抜粋)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /** * Ajax 通信が成功した際に呼び出されます。 * @param {object} data dataType に従って変換されたオブジェクト * @param {string} textStatus ステータス文字列 * @param {jqXHR} jqXHR XmlHttpRequest のラッパーオブジェクト */ var ajax_onsuccess = function (data, textStatus, jqXHR) { if (data.isSuccess) { // ログイン成功なので、リダイレクト window.location = query.ReturnUrl || '/' ; } else { // ログイン失敗なので、input要素 を再度 有効化 $( 'input' ).removeAttr( 'disabled' ); } }; |
最後に… このブログに興味を持っていただけた方は、 ぜひ 「Facebookページ に いいね!」または 「Twitter の フォロー」 お願いします!!