読者です 読者をやめる 読者になる 読者になる

1.21 jigowatts

Great Scott!

ASP.NET MVCはじめました~アクションフィルターでWindows認証からデータベースに権限問い合わせを行う

概要

イントラネットサイトを構築することを想定して、Windows認証で認証を行い、ログオンユーザ名からデータベースの権限テーブルの照合結果により画面の表示を制御するコードを記述しました。


f:id:sh_yoshida:20141219020919p:plain

環境

Visual Studio 2010
ASP.NET MVC2

今回の要件は

  1. Windows認証によりログオンユーザ名を取得
  2. ログオンユーザ名でデータベース問い合わせを行う
  3. ログオンユーザ名とデータベースのレコードが一致した場合、想定の画面を表示する
  4. 一致しない場合はエラー画面を表示する

として、サンプルコードを書いてみました。

実装

アクションフィルターの用意

認証系の処理ということで、AuthorizeAttributeクラスを継承してカスタム属性クラスを記述しました。このベースクラスはIAuthorizationFilterというアクションフィルターインタフェースの実装クラスですが、MVC5からはIAuthenticationFilterというインタフェースが追加されているのでこちらを使ったほうがいいのかもしれません。
■Mvc2App/Extensions/Filters/AuthorityAttribute.cs

public class AuthorityAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (HttpContext.Current.Session["LogOnUser"] == null)
        {
            if (HttpContext.Current.User.Identity.IsAuthenticated)
            {
                var LogOnUser = HttpContext.Current.User.Identity.Name;
                var userName = LogOnUser.Split('\\');

                try
                {
                    IAuth auth = new Auth();

                    //データベースの権限テーブルを照合する
                    if (!auth.IsAuthenticated(userName[1]))
                    {
                        Logger.Debug(userName + "Login失敗");
                        filterContext.Result = new RedirectResult("~/Error/Show/Unauthorized");
                        return;
                    }

                    HttpContext.Current.Session["LogOnUser"] = LogOnUser;
                }
                catch (IndexOutOfRangeException)
                {
                    filterContext.Result = new RedirectResult("~/Error/Show/LoginFail");
                    return;
                }
                catch (ArgumentException)
                {
                    filterContext.Result = new RedirectResult("~/Error/Show/LoginFail");
                    return;
                }
            }
            else
            {
                Logger.Debug("User identity is not authenticated");
                filterContext.Result = new RedirectResult("~/Error/Show/LoginFail");
                return;
            }

        }
        filterContext.Controller.ViewData["LogOnUser"] = HttpContext.Current.Session["LogOnUser"];
    }
}
コントローラの用意

権限による画面制御を行いたいコントローラクラスまたはアクションメソッドにアクションフィルターを追加します。
今回はHomeControllerクラスに追加したので、このクラスのすべてのアクションメソッドが権限による画面制御の対象となります。

using Mvc2App.Extensions.Filters;

...

[AuthorityAttribute]
public class HomeController : Controller
{
    ...
}

実行結果

Windows認証が通りデータベースの権限も確認できた場合、通常のホーム画面が表示されます。
f:id:sh_yoshida:20141219031821p:plain


Windows認証が通らなかったり、データベースの権限テーブルの照合結果が一致しなかった場合は、エラー画面が表示されます。
f:id:sh_yoshida:20141219020919p:plain


今回はまったポイント

IISにデプロイしたときに「Webサイト/アプリケーション名(localhost:50464/Mvc2App)」となるため、権限エラー時にリダイレクトでErrorコントローラへ制御を渡すときにlocalhost:50464/Errorにリダイレクトされサーバエラーとなってしまいました。
原因はRedirectResultメソッドの引数に指定するurlの文字列に「~(チルダ)」が付いていなかったためで、以下のようにすることで、想定どおりlocalhost:50464/Mvc2App/Errorにリダイレクトされ解決しました。

filterContext.Result = new RedirectResult("~/Error/Show/Unauthorized");

参考
プログラミングMicrosoft ASP.NET MVC 第3版ASP.NET MVC 5 対応版 (マイクロソフト公式解説書)
ASP.NET MVC を使用してイントラネット サイトを作成する方法