91

Estoy creando un sitio web multi-tenancy que aloja páginas para clientes. El primer segmento de la URL será una cadena que identifica al cliente, formada en Global.asax usando el siguiente esquema de enrutamiento de URL:Cómo redirigir a una URL de inicio de sesión dinámica en ASP.NET MVC

"{client}/{controller}/{action}/{id}" 

Esto funciona bien, con direcciones URL como/foo/Home/Índice.

Sin embargo, cuando se utiliza el atributo [Autorizar], quiero redirigir a una página de inicio de sesión que también utiliza el mismo esquema de asignación. Entonces, si el cliente es foo, la página de inicio de sesión sería/foo/Account/Login en lugar de la redirección fija/Account/Login definida en web.config.

MVC usa un HttpUnauthorizedResult para devolver un estado 401 no autorizado, lo que presumo hace que ASP.NET redirija a la página definida en web.config.

¿Alguien sabe ya cómo anular el comportamiento de redirección de inicio de sesión de ASP.NET? ¿O sería mejor redirigir en MVC creando un atributo de autorización personalizado?

EDITAR - Respuesta: después de algo de investigación en la fuente de .Net, decidí que un atributo de autenticación personalizado es la mejor solución:

public class ClientAuthorizeAttribute: AuthorizeAttribute 
{ 
    public override void OnAuthorization(AuthorizationContext filterContext) 
    { 
     base.OnAuthorization(filterContext); 

     if (filterContext.Cancel && filterContext.Result is HttpUnauthorizedResult) 
     { 
      filterContext.Result = new RedirectToRouteResult(
       new RouteValueDictionary 
       { 
        { "client", filterContext.RouteData.Values[ "client" ] }, 
        { "controller", "Account" }, 
        { "action", "Login" }, 
        { "ReturnUrl", filterContext.HttpContext.Request.RawUrl } 
       }); 
     } 
    } 
} 
+2

hacer casi exactamente lo mismo con el enrutamiento, así que necesitaba esto! ¡Gracias! –

+0

Gracias, estaba tratando de descubrir cómo hacer algo similar. – Chance

+0

me dio idea para su propia implementación, ¡muchas gracias! –

Respuesta

30

Creo que el problema principal es que si vas para aprovechar la clase incorporada de FormsAuthentication de ASP.NET (y no hay una buena razón para que no lo haga), algo al final del día va a llamar al FormsAuthentication.RedirectToLoginPage() que va a ver la URL configurada. Solo hay una URL de inicio de sesión, y así es como lo diseñaron.

Mi intento del problema (posiblemente una implementación de Rube Goldberg) sería dejarlo redirigir a una sola página de inicio de sesión en la raíz compartida por todos los clientes, por ejemplo/cuenta/inicio de sesión. Esta página de inicio de sesión no mostraría nada en realidad; inspecciona el parámetro ReturnUrl o algún valor que tengo en la sesión o una cookie que identifica al cliente y lo usa para emitir un redireccionamiento 302 inmediato a la página específica/cliente/cuenta/inicio de sesión. Es una redirección extra, pero probablemente no se note y te permite usar los mecanismos de redirección integrados.

La otra opción es crear su propio atributo personalizado como se describe y evitar todo lo que llame al método RedirectToLoginPage() en la clase FormsAuthentication, ya que lo reemplazará con su propia lógica de redirección. (Puede crear su propia clase que sea similar.) Como es una clase estática, no conozco ningún mecanismo por el que pueda simplemente inyectar su propia interfaz alternativa y hacer que funcione mágicamente con el atributo [Autorizar] existente, que golpes, pero people have done similar things before.

Espero que ayude!

+0

este es probablemente el enfoque más seguro. crear su propio atributo [MyAuthorize] es peligroso. a menos que su compilación verifique que las personas no usen el atributo [Autorizar] incorporado, se arriesga a que las personas (o usted) olviden y usen el incorrecto –

40

En la versión RTM de ASP.NET MVC, falta la propiedad Cancelar. Este código funciona con ASP.NET MVC RTM:

using System; 
using System.Web; 
using System.Web.Mvc; 
using System.Web.Mvc.Resources; 

namespace ePegasus.Web.ActionFilters 
{ 
    public class CustomAuthorize : AuthorizeAttribute 
    { 
     public override void OnAuthorization(AuthorizationContext filterContext) 
     { 
      base.OnAuthorization(filterContext); 
      if (filterContext.Result is HttpUnauthorizedResult) 
      { 
       filterContext.Result = new RedirectToRouteResult(
        new System.Web.Routing.RouteValueDictionary 
         { 
           { "langCode", filterContext.RouteData.Values[ "langCode" ] }, 
           { "controller", "Account" }, 
           { "action", "Login" }, 
           { "ReturnUrl", filterContext.HttpContext.Request.RawUrl } 
         }); 
      } 
     } 
    } 
} 

Editar: Es posible que desee desactivar la loginUrl autenticación de formularios predeterminados en web.config - en caso de que alguien se olvida de que tiene un atributo y los usos de encargo el atributo [Authorize] incorporado por error.

Modifique el valor en la web.config:

<forms loginUrl="~/Account/ERROR" timeout="2880" /> 

A continuación, crea un método de acción 'error' que registra un error y redirige al usuario a la página de inicio de sesión más genérica que tiene.

+2

, asegúrese de agregar {área, nulo} al diccionario (o lo que sea su área se llama) si usa MVC 2 y superior, o de lo contrario será heredado de la página que intentó visitar –

2

Mi solución a este problema fue una clase personalizada ActionResult:

sealed public class RequiresLoginResult : ActionResult 
    { 
     override public void ExecuteResult (ControllerContext context) 
     { 
      var response = context.HttpContext.Response; 

      var url = FormsAuthentication.LoginUrl; 
      if (!string.IsNullOrWhiteSpace (url)) 
       url += "?returnUrl=" + HttpUtility.UrlEncode (ReturnUrl); 

      response.Clear(); 
      response.StatusCode = 302; 
      response.RedirectLocation = url; 
     } 

     public RequiresLoginResult (string returnUrl = null) 
     { 
      ReturnUrl = returnUrl; 
     } 

     string ReturnUrl { get; set; } 
    } 
Cuestiones relacionadas