web-dev-qa-db-ja.com

ログオンページにリダイレクトするのではなく、Authorize属性がカスタム403エラーページを返すようにする方法

[Authorize]属性は素敵で便利なMSの発明であり、現在の問題を解決できることを願っています

具体的には:

現在のクライアントが認証されていない場合-[Authorize]保護されたアクションからログオンページにリダイレクトし、ログオンが成功した後、ユーザーを呼び戻します。これは良いことです。

しかし、現在のクライアントが既に認証されているが特定のアクションの実行を許可されていない場合、必要なのは一般的な403ページを表示することだけです。

コントローラーの本体内で認証ロジックを移動せずに可能ですか?

更新:私が必要とする振る舞いは、このスケッチと意味的に等しいはずです:

public ActionResult DoWork()
{
    if (!NotAuthorized())
    {
        // this should be not redirect, but forwarding 
        return RedirectToAction("403");         
    }

    return View();
}

そのため、リダイレクトは一切行わず、URLは同じままにする必要がありますが、ページのコンテンツは403ページに置き換える必要があります

更新2:この方法でスケッチを実装しました:

[HandleError]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewData["Message"] = "Welcome to ASP.NET MVC!";

        return View();
    }

    [CustomActionFilter]
    public ActionResult About()
    {
        return View();
    }

    public ActionResult Error_403()
    {
        return Content("403");
    }
}

public class CustomActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.Result = new ContentResult { Content = "403" };
    }
}

そして、実行をHomeController.Action_403()に適切に転送する方法を取得できないため、403が表示されます。

更新

filterContext.Result = new ViewResult() { ViewName = "Error_403" };

これは特定のビューテンプレートをレンダリングする方法の答えです...しかし、まだ別のコントローラーを実行する方法がわかりません-とにかく、それは十分な良い解決策です。

51
zerkms

AuthorizeAttribute から派生する独自のクラスを作成し、 AuthorizeCore メソッドをオーバーライドして、必要な承認メカニズムを提供できるはずです。 、コントローラに移動する代わりに属性を使用してカスタム認証コードを適用できます。

認可をさらにきめ細かく制御する必要がある場合は、 IActionFilter インターフェイスの実装を作成することをお勧めします(属性に対して、属性をメソッドに適用します)。これにより、コントローラーに移動する前に呼び出しをインターセプトし、コントローラーメソッドが呼び出される前に代替アクションbeforeを提供できます。

これは、OnActionExecutingインターフェイスで IActionFilterメソッド を実装することで実現されます。コントローラをまったく呼び出さないとロジックが判断し、代わりに処理される ActionResult を提供する場合は、 Result property メソッドに渡される ActionExecutingContext インスタンス。これにより、ActionResultを取得するためにコントローラーメソッドに移動する代わりに、そのActionResultが処理されます。

403エラーコードを返す場合は、ContentResultクラスを使用できません。 ActionResultから派生する独自のクラスを作成し、 ExecuteResult メソッドをオーバーライドして StatusCodeプロパティ を設定する必要があります。 = HttpResponseBase to 403、like so like:

internal class Http403Result : ActionResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        // Set the response code to 403.
        context.HttpContext.Response.StatusCode = 403;
    }
}

public class CustomActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.Result = new Http403Result();
    }
}

もちろん、Http403Resultクラスは、返されるステータスコードを受け入れるコンストラクターを取得しますが、概念は同じままです。

35
casperOne

私がやることは、AuthorizeAttributeをサブクラス化し、そのHandleUnauthorizedRequestをオーバーライドして、ユーザーisが認証された場合にHTTPステータスコード403を返すことです。次に、system.webServer\httpErrorsセクションをWeb.Configに追加して、デフォルトの403をカスタムページに置き換えます(この最後の部分ではIIS 7+が必要です)。

public class MyAuthorizeAttribute : AuthorizeAttribute {
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
        if (filterContext.HttpContext.User.Identity.IsAuthenticated)
            filterContext.Result = new HttpStatusCodeResult(403);
        else
            filterContext.Result = new HttpUnauthorizedResult();
    } 
}

<configuration>
  <system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
      <remove statusCode="403" />
      <error statusCode="403" responseMode="ExecuteURL" path="/Error/MyCustom403page" />
    </httpErrors>
  </system.webServer>
</configuration>
49
zvolkov