Pregunta

Estoy creando un sitio web multi-alquiler, que alberga páginas para clientes. El primer segmento de la URL será una cadena que identifica el cliente, que se define en Global.asax usando el siguiente esquema URL de enrutamiento:

"{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 correlación. Así que si el cliente es foo, la página de inicio de sesión sería / foo / cuenta / Ingreso en lugar de la redirección fija / cuenta / Ingreso definido en web.config.

MVC utiliza un HttpUnauthorizedResult para devolver un estado no autorizado 401, que supongo que hace que ASP.NET para redirigir a la página definido en el web.config.

Así que ¿alguien sabe bien cómo reemplazar el comportamiento de redirección ASP.NET de acceso? ¿O sería mejor volver a dirigir en MVC mediante la creación de 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 }
                });
        }
    }
}
¿Fue útil?

Solución

Creo que el problema principal es que si usted va a aprovecharse de la clase FormsAuthentication incorporado en ASP.NET (y no hay una buena razón por la que no debería), algo al final del día va a llamar FormsAuthentication.RedirectToLoginPage() que se va a mirar la URL configurada. Sólo hay una URL de acceso, nunca, y eso es sólo la forma en que lo diseñaron.

Mi puñalada en el problema (posiblemente una implementación de Rube Goldberg) sería dejar que se redirigir a una página de inicio de sesión en la raíz compartida por todos los clientes, por ejemplo el / cuenta / login. Esta página de inicio de sesión no se muestra nada en realidad; se inspecciona el parámetro ReturnUrl o algún valor que tengo en la sesión o una cookie que identifica al cliente y la utiliza para emitir una inmediata redireccionamiento 302 a la cuenta de la página específica / cliente / / inicio de sesión. Es una redirección extra, pero probablemente no se nota y se le permite utilizar el construido en los mecanismos del cambio de dirección.

La otra opción es crear su propio atributo personalizado como usted describe y evitar cualquier cosa que se llama al método RedirectToLoginPage() en el FormsAuthentication clase, ya que va a sustituir con su propia lógica de redirección. (Es posible crear su propia clase que es similar.) Ya que es una clase estática, no estoy al tanto de cualquier mecanismo por el cual sólo podría inyectar su propia interfaz alternativa y han mágicamente trabajar con el atributo existente [Autorizar], el cual golpes, pero personas han hecho cosas similares antes .

Espero que ayude!

Otros consejos

En la versión RTM de ASP.NET MVC, la propiedad Cancel no se encuentra. 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 personalizado y utiliza el construido en el atributo [Autorizar] por error

Modificar el valor en web.config:

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

A continuación, hacer 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.

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

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

Sin embargo, si se decide utilizar el incorporado en ASP.NET FormsAuthentication, uno puede overide Application_AuthenticateRequest en Global.asax.cs de la siguiente manera:

protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    string url = Request.RawUrl;

    if (url.Contains(("Account/Login"))
    {
        return;
    }

    if (Context.User == null)
    {
        // Your custom tenant-aware logic
        if (url.StartsWith("/foo"))
        {
            // Your custom login page.
            Response.Redirect("/foo/Account/Login");
            Response.End();
            return;
        }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top