Redirigiendo el controlador no autorizado en ASP.NET MVC


Tengo un controlador en ASP.NET MVC que he restringido al rol de administrador:

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

Si un usuario que no está en el rol de Administrador navega a este controlador, se recibe con una pantalla en blanco.

Lo que me gustaría hacer es redirigirlos a la vista que dice "necesita estar en el rol de Administrador para poder acceder a este recurso."

Una forma de hacer esto en la que he pensado es tener un check en cada método de acción en isUserInRole () y si no está en role entonces devuelve esto vista informativa. Sin embargo, tendría que poner eso en cada Acción que rompe el principio SECO y es obviamente engorroso de mantener.

Author: Leniel Maccaferri, 2009-06-10

9 answers

Cree un atributo de autorización personalizado basado en AuthorizeAttribute y anule OnAuthorization para realizar la comprobación de cómo desea que se haga. Normalmente, AuthorizeAttribute establecerá el resultado del filtro en HttpUnauthorizedResult si la comprobación de autorización falla. En su lugar, podría configurarlo como ViewResult (de su vista de error).

EDIT : Tengo un par de entradas de blog que van más detalle:

Ejemplo:

    [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
Author: tvanfosson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-22 15:18:24

Puede trabajar con el overridable HandleUnauthorizedRequest dentro de su costumbre AuthorizeAttribute

Así:

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

También puedes hacer algo como esto:

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

Ahora puedes usarlo en tu método HandleUnauthorizedRequest de esta manera:

filterContext.Result = (new RedirectController()).RedirectToSomewhere();
 24
Author: Leniel Maccaferri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-12 14:45:33

El código de "tvanfosson" me estaba dando "Error al ejecutar la solicitud Hija".. He cambiado la OnAuthorization así:

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

Esto funciona bien y muestro los TempData en la página de error. Gracias a "tvanfosson" por el fragmento de código. Estoy usando autenticación de Windows y _isAuthorized no es más que HttpContext.Usuario.Identidad.Está autenticado...

 9
Author: sajoshi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-29 16:55:44

Tuve el mismo problema. En lugar de averiguar el código MVC, opté por un truco barato que parece funcionar. En mi Global.clase 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
Author: MichaelGG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-12 07:37:49

Este problema me ha acosado durante algunos días, así que al encontrar la respuesta que funciona afirmativamente con la respuesta de tvanfosson anterior, pensé que valdría la pena enfatizar la parte central de la respuesta, y abordar algunas capturas relacionadas.

La respuesta central es esta, dulce y simple:

filterContext.Result = new HttpUnauthorizedResult();

En mi caso heredo de un controlador base, por lo que en cada controlador que hereda de él anulo OnAuthorize:

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

El problema era que en 'YourAuth', probé dos cosas que pensé que no solo funcionarían, sino que también terminarían inmediatamente la solicitud. Bueno, así no es como funciona. Así que primero, las dos cosas que NO funcionan, inesperadamente:

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

No solo no funcionan, tampoco terminan la solicitud. Lo que significa lo siguiente:

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

Bueno, incluso con la respuesta correcta de arriba, el flujo de la lógica todavía continúa! Usted todavía golpeará DoMoreStuff... dentro de una autorización. Así que tenlo en cuenta (DoMore... debería estar en otro lugar pues).

Pero con la respuesta correcta, mientras OnAuthorize flow of logic continúa hasta el final, después de eso realmente obtienes lo que esperas: una redirección a tu página de inicio de sesión (si tienes una establecida en Forms auth en tu webconfig).

Pero inesperadamente, 1) Respuesta.Redirect ("/Login") no funciona: el método de Acción todavía se llama, y 2) FormsAuthentication.RedirectToLoginPage(); hace lo mismo: el método Action todavía es llamado!

Que parece totalmente mal para mí, especialmente con este último: ¿quién habría pensado que FormsAuthentication.RedirectToLoginPage no termina la solicitud, o hacer el equivalente anterior de lo que filterContext.Result = new HttpUnauthorizedResult() does?

 2
Author: Nicholas Petersen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-28 00:01:13

Debería crear su propio atributo Authorize-filter.

Aquí está el mío para estudiar;)

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
Author: Ropstah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-10 17:37:06

Habría dejado esto como un comentario, pero necesito más representante, de todos modos, solo quería mencionar a Nicholas Peterson que tal vez pasar el segundo argumento a la llamada de redirección para decirle que termine la respuesta hubiera funcionado. No es la forma más elegante de manejar esto, pero de hecho funciona.

So

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

En lugar de

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

Así que tendrías esto en tu controlador:

 protected override void OnAuthorization(AuthorizationContext filterContext)
 {
      if(!User.IsInRole("Admin")
      {
          base.OnAuthorization(filterContext);
          filterContext.RequestContext.HttpContext.Response.Redirect("/Login", true);
      }
 }
 1
Author: Lazy Coder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-28 17:43:02

Tal vez se obtiene una página en blanco cuando se ejecuta desde Visual Studio en el servidor de desarrollo utilizando la autenticación de Windows ( tema anterior).

Si implementa en IIS, puede configurar páginas de error personalizadas para códigos de estado específicos, en este caso 401. Añadir httpErrors en system.servidor:

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

Luego crea ErrorController.Método no autorizado y vista personalizada correspondiente.

 1
Author: Mark Meyerovich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:39

En tu Startup.Auth.archivo cs agregue esta línea:

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

Ejemplo:

// 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
Author: Ehren Van Auken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-11 17:24:01