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

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

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

Si un utilisateur qui n'est pas dans le rôle d'Administrateur accède à ce contrôleur, ils sont accueillis avec un écran vide.

Ce que je voudrais faire est de les rediriger vers la Vue qui dit "vous devez être dans le rôle d'Administrateur pour pouvoir accéder à cette ressource."

une façon de faire cela à laquelle j'ai pensé est d'avoir un contrôle dans chaque action méthode sur IsUserInRole() et si ce n'est dans son rôle de renvoyer cette information vue. Cependant, je devrais mettre que dans chaque Action qui casse le principal sec et est évidemment lourd à entretenir.

70
demandé sur Leniel Maccaferri 2009-06-10 21:34:24

9 réponses

créer un attribut d'autorisation personnalisé basé sur AuthorizeAttribute et outrepasser OnAuthorization pour effectuer la vérification comment vous voulez qu'il soit fait. Normalement, AuthorizeAttribute définira le résultat du filtre à HttpUnauthorizedResult si la vérification d'autorisation échoue. Vous pouvez le faire définir à un ViewResult (de votre vue D'erreur) à la place.

EDIT : j'ai un couple de billets de blog qui vont dans plus de détails:

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 */);
        }


    }
68
répondu tvanfosson 2011-03-22 15:18:24

vous pouvez travailler avec la surimprimable HandleUnauthorizedRequest à l'intérieur de votre coutume AuthorizeAttribute

comme ceci:

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 ceci:

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

Maintenant vous pouvez l'utiliser dans votre méthode HandleUnauthorizedRequest de cette façon:

filterContext.Result = (new RedirectController()).RedirectToSomewhere();
24
répondu Leniel Maccaferri 2012-10-12 14:45:33

le code de "tvanfosson "me donnait"erreur d'exécution D'une requête enfant".. J'ai changé L'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 le code. J'utilise l'authentification windows et _isAuthorized n'est rien d'autre que HttpContext.Utilisateur.Identité.IsAuthenticated...

9
répondu sajoshi 2013-01-29 16:55:44

j'ai eu le même problème. Plutôt que de comprendre le code MVC, j'ai opté pour un piratage bon marché qui semble fonctionner. Dans mon Global.classe 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)
5
répondu MichaelGG 2009-06-12 07:37:49

ce problème me hante depuis quelques jours maintenant, donc en trouvant la réponse qui fonctionne avec la réponse de tvanfosson ci-dessus, j'ai pensé qu'il serait utile de mettre l'accent sur la partie centrale de la réponse, et d'adresser quelques attrapes liées ya.

la réponse principale est ceci, doux et simple:

filterContext.Result = new HttpUnauthorizedResult();

dans mon cas, j'hérite d'un contrôleur de base, donc dans chaque contrôleur qui en hérite, j'ai la priorité sur Onauthorized:

protected override void OnAuthorization(AuthorizationContext filterContext)
{
    base.OnAuthorization(filterContext);
    YourAuth(filterContext); // do your own authorization logic here
}

le problème était que dans 'Yourrauth', j'ai essayé deux choses que je pensais ne fonctionnerait pas seulement, mais aussi immédiatement mettre fin à la demande. Eh bien, ce n'est pas comment il fonctionne. Tout d'abord, les deux choses qui ne fonctionnent pas, de façon inattendue:

filterContext.RequestContext.HttpContext.Response.Redirect("/Login"); // doesn't work!
FormsAuthentication.RedirectToLoginPage(); // doesn't work!

non seulement ceux qui ne fonctionnent pas, ils ne mettent pas fin à la demande non plus. Qui signifie ce qui suit:

if (!success) {
    filterContext.Result = new HttpUnauthorizedResult();
}
DoMoreStuffNowThatYouThinkYourAuthorized();

eh Bien, même avec la bonne réponse ci-dessus, le flux de logique encore continue! Tu frapperas encore DoMoreStuff... au sein de Onauthorized. Alors gardez cela à l'esprit (DoMore... devrait être dans un autre donc).

mais avec la bonne réponse, alors que le flux Onauthorizate de logique continue jusqu'à la fin encore, après que vous obtenez vraiment ce que vous attendez: une redirection vers votre page de connexion (si vous avez un jeu dans les formulaires auth dans votre webconfig).

mais de façon inattendue, 1) réponse.Redirect ("/Login") ne fonctionne pas: la méthode D'Action fonctionne toujours est appelée, et 2) FormsAuthentication.RedirectToLoginPage (); fait la même chose: la méthode D'Action est toujours appelée!

ce qui me semble totalement erroné, surtout avec ce dernier: qui aurait pensé que formait L'authentification.RedirectToLoginPage ne termine pas la requête, ou faire l'équivalent ci-dessus de ce filterContext.Résultat = new HttpUnauthorizedResult ()?

2
répondu Nicholas Petersen 2012-11-28 00:01:13

vous devez créer votre propre attribut Authorize-filter.

, Voici la mine à l'étude ;)

Public Class RequiresRoleAttribute : Inherits ActionFilterAttribute
    Private _role As String

    Public Property Role() As String
        Get
            Return Me._role
        End Get
        Set(ByVal value As String)
            Me._role = value
        End Set
    End Property

    Public Overrides Sub OnActionExecuting(ByVal filterContext As System.Web.Mvc.ActionExecutingContext)
        If Not String.IsNullOrEmpty(Me.Role) Then
            If Not filterContext.HttpContext.User.Identity.IsAuthenticated Then
                Dim redirectOnSuccess As String = filterContext.HttpContext.Request.Url.AbsolutePath
                Dim redirectUrl As String = String.Format("?ReturnUrl={0}", redirectOnSuccess)
                Dim loginUrl As String = FormsAuthentication.LoginUrl + redirectUrl

                filterContext.HttpContext.Response.Redirect(loginUrl, True)
            Else
                Dim hasAccess As Boolean = filterContext.HttpContext.User.IsInRole(Me.Role)
                If Not hasAccess Then
                    Throw New UnauthorizedAccessException("You don't have access to this page. Only " & Me.Role & " can view this page.")
                End If
            End If
        Else
            Throw New InvalidOperationException("No Role Specified")
        End If

    End Sub
End Class
1
répondu Ropstah 2009-06-10 17:37:06

aurait laissé ceci comme un commentaire mais j'ai besoin de plus de rep, de toute façon je voulais juste mentionner à Nicholas Peterson que peut-être passer le deuxième argument à L'appel de redirection pour lui dire de mettre fin à la réponse aurait fonctionné. Pas la plus élégante façon de gérer cela, mais il fait le travail.

Donc

filterContext.RequestContext.HttpContext.Response.Redirect("/Login", true);

au lieu de

filterContext.RequestContext.HttpContext.Response.Redirect("/Login);

donc vous avez ceci dans votre contrôleur:

 protected override void OnAuthorization(AuthorizationContext filterContext)
 {
      if(!User.IsInRole("Admin")
      {
          base.OnAuthorization(filterContext);
          filterContext.RequestContext.HttpContext.Response.Redirect("/Login", true);
      }
 }
1
répondu Lazy Coder 2015-07-28 17:43:02

peut-être que vous obtenez une page blanche lorsque vous exécutez à partir de Visual Studio sous le serveur de développement en utilisant l'authentification Windows ( sujet précédent ).

si vous utilisez IIS, vous pouvez configurer des pages d'erreur personnalisées pour des codes d'état spécifiques, dans ce cas 401. Ajouter httpErrors sous système.webServer:

<httpErrors>
  <remove statusCode="401" />
  <error statusCode="401" path="/yourapp/error/unauthorized" responseMode="Redirect" />
</httpErrors>

crée alors ErrorController.Méthode non autorisée et vue personnalisée correspondante.

1
répondu Mark Meyerovich 2017-05-23 12:02:39

dans votre Startup.Auth.ajouter cette ligne:

LoginPath = new PathString("/Account/Login"),

exemple:

// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/Account/Login"),
    Provider = new CookieAuthenticationProvider
    {
        // Enables the application to validate the security stamp when the user logs in.
        // This is a security feature which is used when you change a password or add an external login to your account.  
        OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
        validateInterval: TimeSpan.FromMinutes(30),
        regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
    }
});
-1
répondu Ehren Van Auken 2017-05-11 17:24:01