Come reindirizzare a un URL di accesso dinamico in ASP.NET MVC


96

Sto creando un sito web multi-tenancy che ospita pagine per i clienti. Il primo segmento dell'URL sarà una stringa che identifica il client, definito in Global.asax utilizzando il seguente schema di instradamento dell'URL:

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

Funziona bene, con URL come / foo / Home / Index.

Tuttavia, quando si utilizza l'attributo [Autorizza], desidero reindirizzare a una pagina di accesso che utilizza anche lo stesso schema di mappatura. Quindi, se il client è foo, la pagina di accesso sarebbe / foo / Account / Login invece del reindirizzamento fisso / Account / Login definito in web.config.

MVC utilizza un HttpUnauthorizedResult per restituire uno stato 401 non autorizzato, che presumo faccia reindirizzare ASP.NET alla pagina definita in web.config.

Quindi qualcuno sa come sovrascrivere il comportamento di reindirizzamento dell'accesso ASP.NET? O sarebbe meglio reindirizzare in MVC creando un attributo di autorizzazione personalizzato?

EDIT - Risposta: dopo aver scavato un po 'nella fonte .Net, ho deciso che un attributo di autenticazione personalizzato è la soluzione migliore:

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
facendo quasi la stessa identica cosa con il routing, quindi ne avevo bisogno! Grazie!
Trevor de Koekkoek

Grazie, stavo cercando di capire come fare qualcosa di simile.
Possibilità

mi ha dato un'idea per la propria implementazione, grazie mille!
Alexander Beletsky

3
assicurati di impostare area = null (o nell'area corretta) se usi MVC 2 e versioni successive, altrimenti verrà ereditato dalla pagina che hai tentato di visitare
Simon_Weaver

Qualche modo per farlo senza MVC?
DARKGuy

Risposte:


30

Credo che il problema principale è che se avete intenzione di spalle sul built-in ASP.NET classe FormsAuthentication (e non c'è ragione per bene che non si dovrebbe), qualcosa alla fine della giornata sta per chiamata FormsAuthentication.RedirectToLoginPage()che sta andando per guardare l'URL configurato. C'è solo un URL di accesso, in assoluto, ed è così che l'hanno progettato.

Il mio tentativo al problema (possibilmente un'implementazione di Rube Goldberg) sarebbe quello di lasciarlo reindirizzare a una singola pagina di accesso alla radice condivisa da tutti i client, ad esempio / account / login. Questa pagina di accesso non visualizzerebbe effettivamente nulla; ispeziona il parametro ReturnUrl o un valore che ho nella sessione o un cookie che identifica il client e lo utilizza per emettere un reindirizzamento 302 immediato alla pagina specifica / client / account / login. È un reindirizzamento extra, ma probabilmente non visibile e ti consente di utilizzare i meccanismi di reindirizzamento integrati.

L'altra opzione è creare il tuo attributo personalizzato mentre descrivi ed evitare qualsiasi cosa che richiami il RedirectToLoginPage()metodo sulla FormsAuthenticationclasse, poiché lo sostituirai con la tua logica di reindirizzamento. (Potresti creare una tua classe simile.) Poiché è una classe statica, non sono a conoscenza di alcun meccanismo con cui potresti semplicemente iniettare la tua interfaccia alternativa e farla funzionare magicamente con l'attributo [Autorizza] esistente, che colpi, ma le persone hanno già fatto cose simili prima .

Spero che aiuti!


questo è probabilmente l'approccio più sicuro. creare il proprio attributo [MyAuthorize] è pericoloso. a meno che la tua build non stia verificando che le persone non usano l'attributo [Autorizza] integrato, rischi che le persone (o te stesso) dimentichino e usino quello sbagliato
Simon_Weaver

In alcuni casi potrebbe essere utile eseguire l'override Application_AuthenticateRequest(vedi la mia risposta sotto).
turdus-merula

41

Nella versione RTM di ASP.NET MVC, manca la proprietà Annulla. Questo codice funziona 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 }
                        });
            }
        }
    }
}

Modifica: potresti voler disabilitare l'autenticazione moduli predefinita loginUrl in web.config - nel caso in cui qualcuno dimentichi che hai un attributo personalizzato e utilizzi per errore l'attributo [Autorizza] incorporato.

Modifica il valore in web.config:

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

Quindi crea un metodo di azione "ERRORE" che registra un errore e reindirizza l'utente alla pagina di accesso più generica che hai.


2
assicurati di aggiungere {area, null} al dizionario (o qualunque sia la tua area chiamata) se usi MVC 2 e versioni successive - altrimenti verrà ereditato dalla pagina che hai tentato di visitare
Simon_Weaver

2

La mia soluzione a questo problema era una ActionResultclasse personalizzata :

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

0

Eppure, se si decide di utilizzare il built-in ASP.NET FormsAuthentication, si può overide Application_AuthenticateRequestnel Global.asax.csmodo seguente:

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;
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.