しばやん雑記

ASP.NET とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET MVC 5 で追加された IAuthenticationFilter と Authorize 属性の関係

ASP.NET MVC 4 までは IAuthorizationFilter インターフェースだけが用意されていましたが、MVC 5 からは IAuthenticationFilter インターフェースが追加されました。

名前が似ている二つのインターフェースですが、機能としては明確に異なっているので ASP.NET MVC 5 が正式リリースされる前に理解しておく必要がありそうです。ついでに AuthN と AuthZ についても簡単に復習しておきます。

Authentication (AuthN)

Authentication は日本語にすると認証となります。

認証と言えば Basic 認証や Digest 認証が有名ですが、何をやっているかというと Basic 認証の場合は ID とパスワードから有効なユーザーかどうかを判別しています。たったそれだけです。

そして ASP.NET MVC 5 での IAuthenticationFilter インターフェースの定義は以下のようになっています。

public interface IAuthenticationFilter
{
    void OnAuthentication(AuthenticationContext filterContext);

    void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext);
}

OnAuthentication では実際の認証処理を、OnAuthenticationChallenge では WWW-Authenticate ヘッダの出力を行っておけばいいでしょう。

という訳で、Basic 認証を行う属性のテンプレ的なものを作ってみました。

public class BasicAuthenticationAttribute : FilterAttribute, IAuthenticationFilter
{
    public void OnAuthentication(AuthenticationContext filterContext)
    {
        var authorization = filterContext.HttpContext.Request.Headers["Authorization"];

        if (string.IsNullOrEmpty(authorization))
        {
            filterContext.Result = new HttpUnauthorizedResult();

            return;
        }

        // ヘッダに含まれる ID とパスワードを検証する(省略)

        // 有効な ID とパスワードだった場合には Principal を組み立てる
        var identity = new GenericIdentity(...);

        filterContext.Principal = new GenericPrincipal(identity, null);
    }

    public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
    {
        filterContext.HttpContext.Response.Headers["WWW-Authenticate"] = "Basic realm=\"\"";
    }
}

実行してみると以下のような感じ。ちゃんとブラウザがダイアログを表示してくれます。

f:id:shiba-yan:20130627231600p:plain

今までは IAuthorizationFilter を使って実装していた人は多いんじゃないでしょうか?ASP.NET MVC 5 からは IAuthenticationFilter を使って実装するようにしましょう。

多分、OAuth も実装しやすくなったんじゃないかなと思います。

Authorization (AuthZ)

一方、Authorization は日本語にすると認可となります。

認可と言うのは、リソースへのアクセス権を指定することです。例を挙げると ASP.NET MVC の Authorize 属性には Users や Roles といったプロパティがありますよね。この二つのプロパティで、リソースにアクセスできるユーザーやロールを指定することが出来るようになっています。これが認可です。

そして ASP.NET MVC 5 での IAuthorizationFilter インターフェースの定義は以下のようになっています。

public interface IAuthorizationFilter
{
    void OnAuthorization(AuthorizationContext filterContext);
}

この IAuthorizationFilter インターフェースを実装したものが Authorize 属性です。Authorize 属性は前述したようにユーザーとロールでアクセス権を指定することが出来ます。

例えば、先程作った Basic 認証を行う属性と Authorize 属性を組み合わせて使うことが出来るわけです。

[BasicAuthentication]
public class HomeController : Controller
{
    // Basic 認証が通っていれば誰でも見れる
    public ActionResult Index()
    {
        return View();
    }

    // Admin という ID で通っていないと見れない
    [Authorize(Users = "Admin")]
    public ActionResult AdminOnly()
    {
        return View();
    }
}

この場合、Basic 認証が通っていれば Index アクションは全員が見れますが、AdminOnly アクションはユーザー ID が Admin の場合しか見ることが出来ません。

IAuthenticationFilter を実装した属性を複数用意して、それぞれアクションに指定することで別々の認証方式を使うことが出来るという訳です。ちなみに、ASP.NET にはロールが拡張可能な形で用意されているので、Basic 認証を使っていても実現することが出来ます。

最近 RoleProvider を拡張して独自のロールを実装したので、また時間があれば書きたいと思います。