74 votes

Redirection d'un contrôleur non autorisé dans ASP.NET MVC

J'ai un contrôleur dans ASP.NET MVC que j'ai limité au rôle d'administrateur :

[Authorize(Roles = "Admin")]
public class TestController : Controller
{
   ...

Si un utilisateur qui n'a pas le rôle d'administrateur se rend sur ce contrôleur, il est accueilli par un écran vide.

Ce que je voudrais faire, c'est les rediriger vers une vue qui dit "vous devez avoir le rôle d'administrateur pour pouvoir accéder à cette ressource".

Une façon de faire à laquelle j'ai pensé est d'avoir une vérification dans chaque méthode d'action sur IsUserInRole() et si ce n'est pas le cas, de retourner cette vue informationnelle. Cependant, je devrais mettre cela dans chaque action, ce qui rompt le principe DRY et est évidemment lourd à maintenir.

70voto

tvanfosson Points 268301

Créez un attribut d'autorisation personnalisé basé sur AuthorizeAttribute et remplacez OnAuthorization pour effectuer le contrôle comme vous le souhaitez. Normalement, AuthorizeAttribute définit le résultat du filtre comme HttpUnauthorizedResult si le contrôle d'autorisation échoue. Vous pouvez faire en sorte qu'il soit défini comme un ViewResult (de votre vue Error) à la place.

EDITAR : J'ai quelques articles de blog qui entrent dans le détail :

Exemple :

    [AttributeUsage( AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false )]
    public class MasterEventAuthorizationAttribute : AuthorizeAttribute
    {
        /// <summary>
        /// The name of the master page or view to use when rendering the view on authorization failure.  Default
        /// is null, indicating to use the master page of the specified view.
        /// </summary>
        public virtual string MasterName { get; set; }

        /// <summary>
        /// The name of the view to render on authorization failure.  Default is "Error".
        /// </summary>
        public virtual string ViewName { get; set; }

        public MasterEventAuthorizationAttribute()
            : base()
        {
            this.ViewName = "Error";
        }

        protected void CacheValidateHandler( HttpContext context, object data, ref HttpValidationStatus validationStatus )
        {
            validationStatus = OnCacheAuthorization( new HttpContextWrapper( context ) );
        }

        public override void OnAuthorization( AuthorizationContext filterContext )
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException( "filterContext" );
            }

            if (AuthorizeCore( filterContext.HttpContext ))
            {
                SetCachePolicy( filterContext );
            }
            else if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
            {
                // auth failed, redirect to login page
                filterContext.Result = new HttpUnauthorizedResult();
            }
            else if (filterContext.HttpContext.User.IsInRole( "SuperUser" ))
            {
                // is authenticated and is in the SuperUser role
                SetCachePolicy( filterContext );
            }
            else
            {
                ViewDataDictionary viewData = new ViewDataDictionary();
                viewData.Add( "Message", "You do not have sufficient privileges for this operation." );
                filterContext.Result = new ViewResult { MasterName = this.MasterName, ViewName = this.ViewName, ViewData = viewData };
            }

        }

        protected void SetCachePolicy( AuthorizationContext filterContext )
        {
            // ** IMPORTANT **
            // Since we're performing authorization at the action level, the authorization code runs
            // after the output caching module. In the worst case this could allow an authorized user
            // to cause the page to be cached, then an unauthorized user would later be served the
            // cached page. We work around this by telling proxies not to cache the sensitive page,
            // then we hook our custom authorization code into the caching mechanism so that we have
            // the final say on whether a page should be served from the cache.
            HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
            cachePolicy.SetProxyMaxAge( new TimeSpan( 0 ) );
            cachePolicy.AddValidationCallback( CacheValidateHandler, null /* data */);
        }

    }

27voto

Leniel Macaferi Points 38324

Vous pouvez travailler avec la fonction HandleUnauthorizedRequest à l'intérieur de votre AuthorizeAttribute

Comme ça :

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    // Returns HTTP 401 by default - see HttpUnauthorizedResult.cs.
    filterContext.Result = new RedirectToRouteResult(
    new RouteValueDictionary 
    {
        { "action", "YourActionName" },
        { "controller", "YourControllerName" },
        { "parameterName", "YourParameterValue" }
    });
}

Vous pouvez aussi faire quelque chose comme ça :

private class RedirectController : Controller
{
    public ActionResult RedirectToSomewhere()
    {
        return RedirectToAction("Action", "Controller");
    }
}

Vous pouvez maintenant l'utiliser dans votre HandleUnauthorizedRequest de cette façon :

filterContext.Result = (new RedirectController()).RedirectToSomewhere();

10voto

sajoshi Points 1993

Le code de "tvanfosson" me donnait "Error executing Child Request" J'ai modifié le OnAuthorization comme ceci :

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

        if (!_isAuthorized)
        {
            filterContext.Result = new HttpUnauthorizedResult();
        }
        else if (filterContext.HttpContext.User.IsInRole("Administrator") || filterContext.HttpContext.User.IsInRole("User") ||  filterContext.HttpContext.User.IsInRole("Manager"))
        {
            // is authenticated and is in one of the roles 
            SetCachePolicy(filterContext);
        }
        else
        {
            filterContext.Controller.TempData.Add("RedirectReason", "You are not authorized to access this page.");
            filterContext.Result = new RedirectResult("~/Error");
        }
    }

Cela fonctionne bien et je montre le TempData sur la page d'erreur. Merci à "tvanfosson" pour l'extrait de code. J'utilise l'authentification Windows et _isAuthorized n'est rien d'autre que HttpContext.User.Identity.IsAuthenticated...

5voto

MichaelGG Points 8202

J'ai eu le même problème. Plutôt que de comprendre le code MVC, j'ai opté pour une solution bon marché qui semble fonctionner. Dans ma classe Global.asax :

member x.Application_EndRequest() =
  if x.Response.StatusCode = 401 then 
      let redir = "?redirectUrl=" + Uri.EscapeDataString x.Request.Url.PathAndQuery
      if x.Request.Url.LocalPath.ToLowerInvariant().Contains("admin") then
          x.Response.Redirect("/Login/Admin/" + redir)
      else
          x.Response.Redirect("/Login/Login/" + redir)

2voto

Gabriel Mongeon Points 3521

Regardez ça. question/réponse Il est simple et fonctionne parfaitement. Il peut être étendu en fonction de vos besoins.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X