web-dev-qa-db-fra.com

Comment faire pour que l'attribut Autoriser renvoie la page d'erreur 403 personnalisée au lieu de la rediriger vers la page de connexion

[Authorize] l'attribut est une invention MS agréable et pratique, et j'espère qu'il pourra résoudre les problèmes que j'ai maintenant

Pour être plus précis:

Lorsque le client actuel n'est pas authentifié - [Authorize] redirige l'action sécurisée vers la page de connexion et une fois la connexion réussie - ramène l'utilisateur, c'est bien.

Mais lorsque le client actuel est déjà authentifié mais n'est pas autorisé à exécuter une action spécifique - tout ce dont j'ai besoin est simplement d'afficher ma page 403 générale.

Est-ce possible sans déplacer la logique d'autorisation dans le corps du contrôleur?

pdate: Le comportement dont j'ai besoin devrait être sémantiquement égal à ce croquis:

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

    return View();
}

donc - il ne doit pas y avoir de redirection et l'url doit rester la même, mais le contenu de la page doit être remplacé par 403 pages

pdate 2: J'ai implémenté le sketch de cette façon:

[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" };
    }
}

Et ne peut pas savoir comment transmettre correctement l'exécution à HomeController.Action_403 () pour qu'il affiche 403.

mise à jour:

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

c'est donc une réponse sur la façon de rendre un modèle de vue spécifique ... mais je n'ai toujours aucune idée de comment exécuter un autre contrôleur - de toute façon, c'est une assez bonne solution.

51
zerkms

Vous devriez pouvoir créer votre propre classe qui dérive de AuthorizeAttribute et remplacer la méthode AuthorizeCore pour fournir le mécanisme d'autorisation que vous souhaitez , afin que vous puissiez appliquer votre code d'autorisation personnalisé en utilisant un attribut au lieu de le déplacer dans le contrôleur.

Si vous avez besoin d'un contrôle plus fin sur l'autorisation, je vous recommande de créer une implémentation de l'interface IActionFilter (sur un attribut, puis appliquez l'attribut à vos méthodes). Cela vous permettra d'intercepter les appels avant qu'ils ne parviennent au contrôleur et de fournir des actions alternatives avant d'appeler votre méthode de contrôleur.

Ceci est réalisé en implémentant la méthode OnActionExecuting sur l'interface IActionFilter. Si votre logique détermine que vous ne devez pas du tout appeler le contrôleur et que vous souhaitez fournir un ActionResult à traiter à la place, vous devez définir le Result property sur l'instance ActionExecutingContext passée dans la méthode. Ce faisant, ce ActionResult est traité au lieu d'aller à la méthode du contrôleur pour obtenir un ActionResult.

Si vous souhaitez renvoyer un code d'erreur 403, vous ne pouvez pas utiliser la classe ContentResult. Vous devrez créer votre propre classe qui dérive de ActionResult et remplacer la méthode ExecuteResult pour définir la propriété StatusCode = sur le HttpResponseBase à 403, comme ceci:

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();
    }
}

Bien sûr, vous pouvez généraliser le Http403Result classe pour prendre un constructeur qui acceptera le code d'état que vous souhaitez renvoyer, mais le concept reste le même.

35
casperOne

Ce que je ferais, c'est sous-classe AuthorizeAttribute et remplacer son HandleUnauthorizedRequest pour retourner le code d'état HTTP 403 si l'utilisateur est authentifié. J'ajouterais alors une section system.webServer\httpErrors à mon Web.Config pour remplacer le 403 par défaut avec ma page personnalisée (cette dernière partie nécessite IIS 7+). Voici comment:

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