web-dev-qa-db-ja.com

ASP.NET MVC-ログインページに不正なエラーを表示する方法

私のASP.NET MVCアプリでは、ほとんどのコントローラーが

[Authorize(Roles="SomeGroup")]

ユーザーが何かへのアクセスを許可されていない場合、ユーザーは「〜/ Login」に送信されます。これは、私のアカウントコントローラーのログインアクションです。

承認されていないためにユーザーがログインページに到達したと判断して、適切なエラーを表示するにはどうすればよいですか?

45
Ronnie Overby

?ReturnUrl=クエリ文字列値を検索するか、独自の認証フィルターを作成して、理由を示すフィールドをTempDataに設定できます。

トリックを実行する単純なカスタムフィルターを次に示します。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CustomAuthorizeAttribute : AuthorizeAttribute
{

    // NOTE: This is not thread safe, it is much better to store this
    // value in HttpContext.Items.  See Ben Cull's answer below for an example.
    private bool _isAuthorized;

    protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
    {
        _isAuthorized = base.AuthorizeCore(httpContext);
        return _isAuthorized;
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        if(!_isAuthorized)
        {
            filterContext.Controller.TempData.Add("RedirectReason", "Unauthorized");
        }
    }
}

次に、あなたの見解では、次のようなことができます:

@if(TempData["RedirectReason"] == "Unauthorized")
{
    <b>You don't have permission to access that area</b>
}

(これらの魔法の文字列よりも優れたアプローチをお勧めしますが、要点はわかります)

28
Ben Scheirman

UPDATE(2015年6月):@daniel-lidströmは、ASP.NET MVCアプリケーションでResponse.Redirectを使用してはならないことを正しく指摘しています。理由の詳細については、次のリンクを参照してください: Response.RedirectとASP.NET MVC –混在しない

UPDATE(2014年9月):HandleUnauthorizedRequestがAuthorizeAttributeにいつ追加されたかわかりませんが、どちらの方法でもAuthorizeRedirectコードを調整できました小さくてシンプルなものに。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeRedirect : AuthorizeAttribute
{
    public string RedirectUrl = "~/Error/Unauthorized";

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        base.HandleUnauthorizedRequest(filterContext);

        if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
        {
            filterContext.Result = new RedirectResult(RedirectUrl);
        }
    }
}

以下の元の回答(まだ完全に機能しています)

承認パイプラインがどのように機能するかについての洞察がまだ得られるため、この回答はここに残しました。

まだここに上陸している人のために、ユーザーがログインしているが許可されていない場合に、無許可のページに自動的にリダイレクトするように、Ben Scheirmanの回答を編集しました。名前パラメーターRedirectUrlを使用してリダイレクトパスを変更できます。

EDIT:Tarynn および [〜#のアドバイスにより、ソリューションをスレッドセーフにしました〜] msdn [〜#〜]

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeRedirect : AuthorizeAttribute
{
    private const string IS_AUTHORIZED = "isAuthorized";

    public string RedirectUrl = "~/error/unauthorized";

    protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
    {
        bool isAuthorized = base.AuthorizeCore(httpContext);

        httpContext.Items.Add(IS_AUTHORIZED, isAuthorized);

        return isAuthorized;
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        var isAuthorized = filterContext.HttpContext.Items[IS_AUTHORIZED] != null 
            ? Convert.ToBoolean(filterContext.HttpContext.Items[IS_AUTHORIZED]) 
            : false;

        if (!isAuthorized && filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
        {
            filterContext.RequestContext.HttpContext.Response.Redirect(RedirectUrl);
        }
    }
}
76
Ben Cull

Ben Cullのメソッドはうまく機能しますが、2つのAuthorizeAttributeクラスがあることを思い出してください。1つはSystem.Web.HTTP(Web APIが使用)にあり、もう1つはSystem.Web.Mvcにあります。ベンのメソッドはSystem.Web.Mvcクラスを使用します。わかりやすくするために、完全修飾パスを使用することをお勧めします。

MVCとともにWeb APIを使用している場合は、2つのフィルターを実装する必要があります。

public class AuthorizeRedirectMVCAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        base.HandleUnauthorizedRequest(filterContext);

        if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
        {
            filterContext.Result = new RedirectResult("~/Account/AccessDenied");
        }
    }
}

public class AuthorizeRedirectAPIAttribute : System.Web.Http.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
    {
        base.HandleUnauthorizedRequest(actionContext);

        if (actionContext.RequestContext.Principal.Identity.IsAuthenticated)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden);
        }
    }
}

Asp.netを使用すると、MVCコントローラーをAPIフィルターで装飾できることに注意してください。期待どおりに機能しないため、属性名を明示的にしてください。

5

コントローラがあり、コードにURLを含めたくない場合は、この方法でリダイレクトすることもできます。ブラウザーのアドレスバーのURLは変更されないため、ユーザーは未承認のページのURLを見ることができません。これはMVC 3で記述されています。この方法は、ログインページにリダイレクトする場合や、認証されていないことを伝えるためにページにリダイレクトする場合にも機能します。一部のユーザーには権限がないプログラムのセクションがありましたが、ユーザーはログインしていたため、これを使用しました。

public class AuthorizedRedirect : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        bool isAuthorized = base.AuthorizeCore(httpContext);
        return isAuthorized;
    }
protect override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    filterContext.RequestContext.RouteData.Values["controller"] = "error";
    filterContext.Result = new ViewResult { ViewName = "unauthorized" };
}
4
divide_byzero

そして、FormsAuthentication設定を利用するさらにシンプルなバージョンです。契約に不慣れな方のために、Contract.Requiresは.NET 4の追加です。 Code Contracts を使用することの長所と短所。

public class RequiresAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        Contract.Requires(filterContext != null);

        HttpContextBase context = filterContext.RequestContext.HttpContext;

        if (context.User.Identity.IsAuthenticated)
        {
            // user does not possess the required role permission
            string url = context.GetCustomErrorUrl(401);
            context.Response.Redirect(url);
        }
        else
        {

            // redirect the user to the login page
            string extraQueryString  = context.Request.RawUrl;
            FormsAuthentication.RedirectToLoginPage(extraQueryString);
        }
    }
}
2
mousedoc

コントローラーがない場合でも、divide_byzeroの回答からさらに進んで、HandleUnauthorizedRequestを使用してリダイレクトを変更できます。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class AuthoriseRedirect : AuthorizeAttribute
    {
        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            filterContext.RequestContext.HttpContext.Response.Redirect("UrlToRedirectTo");
        }
    }

長期間にわたってMVCに変換するレガシーWebフォームサイトがある場合に役立ちます。

1
Bindi

私はブライアンヴァンダープラッツが投稿したものを気に入って、いくつかの改良を加えました:

/// <summary>
/// Authorize or redirect to an unauthorized MVC action if the user does not have the required roles
/// (an unauthenticated user will be redirected to the defualt sign in action)
/// <para>Decorate an action or a controller like this [AuthorizeAndRedirect(Roles = "RoleName")]</para>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class AuthorizeOrRedirectAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        base.HandleUnauthorizedRequest(filterContext);

        if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
        {
            var routeData = new RouteData();
            routeData.Values.Add("controller", "Error");
            routeData.Values.Add("action", "Unauthorized");
            filterContext.Result = new RedirectToRouteResult(routeData.Values);
        }
    }
}

/// <summary>
/// Authorize or redirect to an unauthorized API action if the user does not have the required roles
/// (an unauthenticated user will be redirected to the defualt sign in action)
/// <para>Decorate an action or a controller like this [AuthorizeAndRedirect(Roles = "RoleName")]</para>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class AuthorizeOrRedirectApiFilterAttribute : System.Web.Http.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
    {
        base.HandleUnauthorizedRequest(actionContext);

        if (actionContext.RequestContext.Principal.Identity.IsAuthenticated)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
        }
    }
}
0
Yovav