ASP.NET MVC: cómo mostrar un error no autorizado en la página de inicio de sesión?

En mi aplicación ASP.NET MVC, tengo la mayoría de los controladores decorados con

[Authorize(Roles="SomeGroup")] 

Cuando un usuario no está autorizado a acceder a algo, se lo envía a “~ / Login”, que es la acción de inicio de sesión en mi controlador de cuenta.

¿Cómo puedo determinar si un usuario ha llegado a la página de inicio de sesión porque no está autorizado para que pueda mostrar un error apropiado?

Puede buscar el valor ?ReturnUrl= , o puede crear su propio filtro de autorización y establecer un campo en TempData indique el motivo.

Aquí hay un filtro personalizado simple que hará el truco:

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class CustomAuthorizeAttribute : AuthorizeAttribute { // NOTE: This is not thread safe, it is much better to store this // value in HttpContext.Items. See Ben Cull's answer below for an example. private bool _isAuthorized; protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext) { _isAuthorized = base.AuthorizeCore(httpContext); return _isAuthorized; } public override void OnAuthorization(AuthorizationContext filterContext) { base.OnAuthorization(filterContext); if(!_isAuthorized) { filterContext.Controller.TempData.Add("RedirectReason", "Unauthorized"); } } } 

Entonces en su opinión, puede hacer algo como esto:

 @if(TempData["RedirectReason"] == "Unauthorized") { You don't have permission to access that area } 

(Aunque recomendaría un enfoque mejor que estas cadenas mágicas, pero entiendes el punto)

ACTUALIZACIÓN (junio de 2015): @ daniel-lidström ha señalado correctamente que no debe usar Response.Redirect en una aplicación ASP.NET MVC. Para obtener más información acerca de por qué, consulte este enlace: Response.Redirect y ASP.NET MVC – No mezclar .

ACTUALIZACIÓN (septiembre de 2014): no estoy seguro de cuándo HandleUnauthorizedRequest se agregó a AuthorizeAttribute, pero de cualquier manera he podido refinar el código de AuthorizeRedirect en algo más pequeño y simple.

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class AuthorizeRedirect : AuthorizeAttribute { public string RedirectUrl = "~/Error/Unauthorized"; protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { base.HandleUnauthorizedRequest(filterContext); if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated) { filterContext.Result = new RedirectResult(RedirectUrl); } } } 

Respuesta original a continuación (sigue siendo completamente funcional)

Dejé esta respuesta aquí, ya que todavía te da una idea de cómo funciona la canalización de Autorización.

Para cualquier persona que aún aterrice aquí, he editado la respuesta de Ben Scheirman para redirigir automáticamente a una página no autorizada cuando el usuario está conectado pero no autorizado. Puede cambiar la ruta de redireccionamiento utilizando el parámetro de nombre RedirectUrl.

EDITAR: He hecho que la solución sea segura para subprocesos gracias a los consejos de Tarynn y MSDN

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class AuthorizeRedirect : AuthorizeAttribute { private const string IS_AUTHORIZED = "isAuthorized"; public string RedirectUrl = "~/error/unauthorized"; protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext) { bool isAuthorized = base.AuthorizeCore(httpContext); httpContext.Items.Add(IS_AUTHORIZED, isAuthorized); return isAuthorized; } public override void OnAuthorization(AuthorizationContext filterContext) { base.OnAuthorization(filterContext); var isAuthorized = filterContext.HttpContext.Items[IS_AUTHORIZED] != null ? Convert.ToBoolean(filterContext.HttpContext.Items[IS_AUTHORIZED]) : false; if (!isAuthorized && filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated) { filterContext.RequestContext.HttpContext.Response.Redirect(RedirectUrl); } } } 

El método de Ben Cull funciona bien, pero recuerde que hay dos clases AuthorizeAttribute: una en System.Web.HTTP (utilizada por Web API) y la otra en System.Web.Mvc. El método de Ben usa la clase System.Web.Mvc. Para mayor claridad, sugiero usar la ruta completa.

Si está utilizando la API web junto con MVC, deberá implementar dos filtros:

 public class AuthorizeRedirectMVCAttribute : System.Web.Mvc.AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { base.HandleUnauthorizedRequest(filterContext); if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated) { filterContext.Result = new RedirectResult("~/Account/AccessDenied"); } } } public class AuthorizeRedirectAPIAttribute : System.Web.Http.AuthorizeAttribute { protected override void HandleUnauthorizedRequest(HttpActionContext actionContext) { base.HandleUnauthorizedRequest(actionContext); if (actionContext.RequestContext.Principal.Identity.IsAuthenticated) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden); } } } 

Tenga en cuenta que asp.net le permitirá decorar su controlador MVC con un filtro de API; simplemente no funcionará de la manera que espera, así que mantenga sus nombres de atributo explícitos.

Si tienes un controlador y no quieres tener una url en tu código, puedes redireccionar de esta manera también. No cambiará la URL en la barra de direcciones del navegador para que el usuario nunca vea la URL de la página no autorizada. Esto fue escrito en MVC 3. Este método también funcionará si desea redirigirlos a una página de inicio de sesión o si desea redirigirlos a una página para indicarles que no están autorizados. Tenía una sección en el progtwig que algunos usuarios no tenían derechos, pero estaban conectados, así que esto es lo que usé.

 public class AuthorizedRedirect : AuthorizeAttribute { protected override bool AuthorizeCore(HttpContextBase httpContext) { bool isAuthorized = base.AuthorizeCore(httpContext); return isAuthorized; } protect override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { filterContext.RequestContext.RouteData.Values["controller"] = "error"; filterContext.Result = new ViewResult { ViewName = "unauthorized" }; } 

Y una versión aún más simple que utiliza la configuración de FormsAuthentication. Para aquellos que no están familiarizados con Contract, Contract.Requires es una adición a .NET 4. Pros y contras de usar contratos de código .

 public class RequiresAttribute : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { Contract.Requires(filterContext != null); HttpContextBase context = filterContext.RequestContext.HttpContext; if (context.User.Identity.IsAuthenticated) { // user does not possess the required role permission string url = context.GetCustomErrorUrl(401); context.Response.Redirect(url); } else { // redirect the user to the login page string extraQueryString = context.Request.RawUrl; FormsAuthentication.RedirectToLoginPage(extraQueryString); } } } 

Yendo más allá de la respuesta de divide_byzero incluso si no tiene un controlador, puede usar HandleUnauthorizedRequest para cambiar la redirección.

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class AuthoriseRedirect : AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { filterContext.RequestContext.HttpContext.Response.Redirect("UrlToRedirectTo"); } } 

¡Es útil si tiene un sitio heredado de formularios web que va a convertir a MVC durante un período de tiempo más largo …!

Me gusta lo que publicó Brian Vander Plaats, solo agregué algunas mejoras:

 ///  /// Authorize or redirect to an unauthorized MVC action if the user does not have the required roles /// (an unauthenticated user will be redirected to the defualt sign in action) /// Decorate an action or a controller like this [AuthorizeAndRedirect(Roles = "RoleName")] ///  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public class AuthorizeOrRedirectAttribute : System.Web.Mvc.AuthorizeAttribute { protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { base.HandleUnauthorizedRequest(filterContext); if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated) { var routeData = new RouteData(); routeData.Values.Add("controller", "Error"); routeData.Values.Add("action", "Unauthorized"); filterContext.Result = new RedirectToRouteResult(routeData.Values); } } } ///  /// Authorize or redirect to an unauthorized API action if the user does not have the required roles /// (an unauthenticated user will be redirected to the defualt sign in action) /// Decorate an action or a controller like this [AuthorizeAndRedirect(Roles = "RoleName")] ///  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public class AuthorizeOrRedirectApiFilterAttribute : System.Web.Http.AuthorizeAttribute { protected override void HandleUnauthorizedRequest(HttpActionContext actionContext) { base.HandleUnauthorizedRequest(actionContext); if (actionContext.RequestContext.Principal.Identity.IsAuthenticated) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); } } }