web-dev-qa-db-ja.com

MVCの不正なページアクセスをカスタムビューにリダイレクトする

アクセスがさまざまなロールに基づいているMVC Webサイトがあります。ユーザーがシステムにログインすると、承認されているページへのナビゲーションが表示されます。ただし、一部のユーザーは直接URLを使用してページにアクセスしようとする場合があります。その場合、システムは自動的にそれらをログインページにリダイレクトします。ログインページの代わりに、別のビュー(未承認)にリダイレクトします。

Web.Configには次のエントリがあります。

    <customErrors mode="On">
      <error statusCode="401" redirect="~/Home/Unauthorized" />
      <error statusCode="404" redirect="~/Home/PageNotFound" />
    </customErrors>
    <authentication mode="Forms">
<forms name="Development" loginUrl="~/Account/Login" cookieless="UseCookies" timeout="120"></forms>
    </authentication>

これらのルートもGlobal.asax.csに登録しました。

routes.MapRoute(
    name: "Unauthorized",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Unauthorized", id = UrlParameter.Optional }
   );


routes.MapRoute(
    name: "PageNotFound",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "PageNotFound", id = UrlParameter.Optional }
    );

それで十分でしょうか?

15
user2739418

次の変更で動作しています

public class CustomAuthorize : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        //filterContext.Result = new HttpUnauthorizedResult(); // Try this but i'm not sure
          filterContext.Result = new RedirectResult("~/Home/Unauthorized");
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (this.AuthorizeCore(filterContext.HttpContext))
        {
            base.OnAuthorization(filterContext);
        }
        else
        {
            this.HandleUnauthorizedRequest(filterContext);
        }
    }

}

そして、以下のようにコントローラーまたはアクションに適用します:

[CustomAuthorize(Roles = "Admin")]

上記のアプローチでは、すべてのコントローラー/アクションを再確認し、Authorized属性を変更する必要があります!また、いくつかのテストが必要になります。

Web.Configルートが同じように機能しない理由がMVCドキュメントで説明されている理由はまだわかりません。 MVC 4で何かが変わったかもしれません!

17
user2739418

いくつかの調査の後、この問題に対する最も簡単な答えは、jbbiによるものと非常によく似たカスタム認証を作成することだと思います(ただし、「新しいHttpUnauthorizedResult()」は内部的に自動的にログインにリダイレクトするため、 mvc 5 in identity)

public class CustomAuthorize : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            //if not logged, it will work as normal Authorize and redirect to the Login
            base.HandleUnauthorizedRequest(filterContext);

        }
        else
        {
            //logged and wihout the role to access it - redirect to the custom controller action
            filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Error", action = "AccessDenied" }));
        }
    }
}

使用法はデフォルトのAuthorizeと同じです:

[CustomAuthorize(Roles = "Administrator")]

次に、正しいことをするために、エラーページのHttpコードを送信することを忘れないでください。 f.e.コントローラーでこのように。

public ActionResult AccessDenied()
{
    Response.StatusCode = 403;
    return View();
}

それは簡単で、うまくいき、私(.net mvc rookie)でさえこれを理解しています。

注: 401コードでは同じように機能しません。常に401を引き継ぎ、内部的にログインにリダイレクトします。しかし、私の場合、定義上、403もフィッティングです。

28
The Vojtisek

おそらくこれを処理する最適な方法は、追加のアクションフィルターを作成することです。これにより、ユーザーが指定されたロールに属していない場合、指定されたエラーページにリダイレクトされます。そのため、このメソッドには両方のフィルターが適用されます。[Authorize](ロールなし)により、認証されていないユーザーから保護し、ログインページにリダイレクトします。そして、ロールを持つカスタム属性。これと同様のコード(テストなし):

public class RoleFilterAttribute : ActionFilterAttribute
{
    public string Role { get; set; }
    public override void OnActionExecuting(ActionExecutingContext ctx)
    {
        // Assume that we have user identity because Authorize is also
        // applied
        var user = ctx.HttpContext.User;
        if (!user.IsInRole(Role))
        {
            ctx.Result = new RedirectResult("url_needed_here");
        }
    }
}

[Authorize]と[RoleFilter]の両方をアクションに適用します...

お役に立てれば!

4
eiximenis

デフォルトのAuthorizeフィルターを継承する独自のAuthorizeフィルター属性を作成する必要があると思います

public class CustomAuthorize: AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
       filterContext.Result = new HttpUnauthorizedResult(); // Try this but i'm not sure
    }
}
2
binard