2009-09-30 31 views
43

En mi aplicación ASP.NET MVC, tengo la mayoría de los controladores decoradas conASP.NET MVC: cómo mostrar un error no autorizado en la página de inicio de sesión?

[Authorize(Roles="SomeGroup")] 

Cuando un usuario no está autorizado a acceder a algo, son enviados a "~/Inicio de sesión", que es la acción de sesión en mi cuenta controlador.

¿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?

Respuesta

28

Puede buscar el valor de la cadena de consulta ?ReturnUrl=, o puede crear su propio filtro de autorización & establezca un campo en TempData indicando el motivo.

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

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

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

(Aunque me gustaría recomendar una mejor enfoque de estas cadenas mágicas, pero usted consigue el punto)

+0

Fácil de implementar y funciona. Gracias. –

+0

Gracias Ben, ¡implemento lo mismo de mi lado y trabajo muy bien! –

+0

Se acepta esta respuesta, pero no es segura para subprocesos, consulte la respuesta a continuación para obtener más detalles. Actualice su código para que sea seguro para subprocesos, muchos desarrolladores pueden no leer otras respuestas y usar la suya como la mejor. –

74

ACTUALIZACIÓN (jun 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 and ASP.NET MVC – Do Not Mix.

ACTUALIZACIÓN (sep 2014): no estoy seguro de que cuando se añadió a la HandleUnauthorizedRequest AuthorizeAttribute, pero de cualquier manera he sido capaz de refinar el código AuthorizeRedirect en algo más pequeño y más 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 Abajo (aún completamente funcional)

He dejado esta respuesta aquí, ya que todavía le da una idea de cómo las obras de 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.

EDIT: He hecho las soluciones compatibles con el proceso 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); 
     } 
    } 
} 
+5

De MSDN: derivados de AuthorizeAttribute Si derivados de la clase AuthorizeAttribute, el tipo derivado debe ser seguro para subprocesos. Por lo tanto, no almacene estado en una instancia del tipo en sí mismo (por ejemplo, en un campo de instancia) a menos que ese estado se aplique a todas las solicitudes. En su lugar, almacene el estado por solicitud en la propiedad Elementos, a la que se puede acceder a través de los objetos de contexto pasados ​​a AuthorizeAttribute. no es _isAutorizado un campo de instancia? – Tarynn

+0

Vaya, buena captura, actualizaré la solución anterior para que coincida. –

+0

¿Funciona este método con FormsAuth y la redirección predeterminada? –

4

Si tiene un controlador y no desea tener una URL en que se codifican puede redirigir 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 programa 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" }; 
} 
+0

Como otra variación para mostrar el mensaje en una Vista compartida, p. 'var vr = new ViewResult(); vr.ViewName = "Información"; vr.ViewBag.Message = "No está autorizado para esta página; contáctenos."; filterContext.Result = vr; ' – subsci

2

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 Code Contracts.

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

Yendo más allá de la respuesta de divide_byzero incluso si usted no tiene un controlador puede seguir utilizando el 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 muy útil si usted tiene un sitio de memoria formularios web que va a convertirse al MVC durante un período de tiempo más largo .....!

4

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 de Web junto con MVC, que tendrá que 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 API - que acaba de ganar' t funciona de la manera que esperas, por lo que debes mantener tus nombres de atributo explícitos.

0

me gusta lo publicado Brian Vander Plaats, acaba de añadir algunas mejoras:

/// <summary> 
/// 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) 
/// <para>Decorate an action or a controller like this [AuthorizeAndRedirect(Roles = "RoleName")]</para> 
/// </summary> 
[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); 
     } 
    } 
} 

/// <summary> 
/// 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) 
/// <para>Decorate an action or a controller like this [AuthorizeAndRedirect(Roles = "RoleName")]</para> 
/// </summary> 
[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); 
     } 
    } 
} 
Cuestiones relacionadas