So leiten Sie in ASP.NET MVC zu einer dynamischen Anmelde-URL um

96

Ich erstelle eine mandantenfähige Website, auf der Seiten für Kunden gehostet werden. Das erste Segment der URL ist eine Zeichenfolge, die den in Global.asax definierten Client anhand des folgenden URL-Routing-Schemas identifiziert:

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

Dies funktioniert gut mit URLs wie / foo / Home / Index.

Wenn Sie jedoch das Attribut [Autorisieren] verwenden, möchte ich zu einer Anmeldeseite umleiten, die ebenfalls dasselbe Zuordnungsschema verwendet. Wenn der Client also foo ist, lautet die Anmeldeseite / foo / Account / Login anstelle der in web.config definierten festen Umleitung / Account / Login.

MVC verwendet ein HttpUnauthorizedResult, um einen nicht autorisierten 401-Status zurückzugeben, von dem ich annehme, dass ASP.NET zu der in web.config definierten Seite umleitet.

Weiß jemand, wie man das ASP.NET-Anmeldeumleitungsverhalten überschreibt? Oder ist es besser, in MVC umzuleiten, indem Sie ein benutzerdefiniertes Berechtigungsattribut erstellen?

BEARBEITEN - Antwort: Nachdem ich mich ein wenig mit der .Net-Quelle befasst hatte, entschied ich, dass ein benutzerdefiniertes Authentifizierungsattribut die beste Lösung ist:

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 }
                });
        }
    }
}
Mike Scott
quelle
2
Ich mache fast genau das Gleiche mit Routing, also brauchte ich das! Vielen Dank!
Trevor de Koekkoek
Danke, ich habe versucht herauszufinden, wie man etwas Ähnliches macht.
Chance
es gab mir eine Idee für die eigene Implementierung, vielen Dank!
Alexander Beletsky
3
achten Sie darauf, Set Bereich = null (oder auf den richtigen Bereich) bei Verwendung von MVC 2 und höher - sonst werden sie von der Seite geerbt bekommen versuchen Sie zu besuchen
Simon_Weaver
Wie geht das ohne MVC?
DARKGuy

Antworten:

30

Ich denke, das Hauptproblem ist, dass, wenn Sie auf die integrierte ASP.NET FormsAuthentication-Klasse zurückgreifen (und es gibt keinen guten Grund, warum Sie dies nicht tun sollten), am Ende des Tages etwas aufgerufen wird, FormsAuthentication.RedirectToLoginPage()das läuft um die eine konfigurierte URL anzusehen. Es gibt immer nur eine Anmelde-URL, und genau so haben sie sie entworfen.

Mein Problem (möglicherweise eine Rube Goldberg-Implementierung) besteht darin, sie auf eine einzelne Anmeldeseite im Stammverzeichnis umleiten zu lassen, die von allen Clients gemeinsam genutzt wird, z. B. / account / login. Auf dieser Anmeldeseite wird eigentlich nichts angezeigt. Es überprüft entweder den ReturnUrl-Parameter oder einen Wert, den ich in der Sitzung erhalten habe, oder ein Cookie, das den Client identifiziert und damit eine sofortige 302-Umleitung zur spezifischen Seite / client / account / login ausgibt. Es ist eine zusätzliche Umleitung, aber wahrscheinlich nicht erkennbar, und Sie können die integrierten Umleitungsmechanismen verwenden.

Die andere Option besteht darin, ein eigenes benutzerdefiniertes Attribut zu erstellen, während Sie alles beschreiben und vermeiden, was die RedirectToLoginPage()Methode für die FormsAuthenticationKlasse aufruft , da Sie sie durch Ihre eigene Umleitungslogik ersetzen. (Sie können eine eigene Klasse erstellen, die ähnlich ist.) Da es sich um eine statische Klasse handelt, ist mir kein Mechanismus bekannt, mit dem Sie einfach Ihre eigene alternative Schnittstelle einfügen und sie auf magische Weise mit dem vorhandenen Attribut [Authorize] arbeiten lassen können Schläge, aber die Leute haben schon früher ähnliche Dinge getan .

Hoffentlich hilft das!

Nicholas Piasecki
quelle
Dies ist wahrscheinlich der sicherste Ansatz. Das Erstellen eines eigenen Attributs [MyAuthorize] ist gefährlich. Wenn Ihr Build nicht überprüft,
ob Benutzer
In einigen Fällen kann es jedoch hilfreich sein, diese zu überschreiben Application_AuthenticateRequest(siehe meine Antwort unten).
Turdus-Merula
41

In der RTM-Version von ASP.NET MVC fehlt die Eigenschaft Abbrechen. Dieser Code funktioniert mit 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 }
                        });
            }
        }
    }
}

Bearbeiten: Möglicherweise möchten Sie die Standard-Formularauthentifizierung loginUrl in web.config deaktivieren. Falls jemand vergisst, dass Sie ein benutzerdefiniertes Attribut haben, und versehentlich das integrierte Attribut [Autorisieren] verwendet.

Ändern Sie den Wert in web.config:

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

Erstellen Sie dann eine Aktionsmethode 'ERROR', die einen Fehler protokolliert und den Benutzer auf die allgemeinste Anmeldeseite umleitet, die Sie haben.

Benutzer134936
quelle
2
Stellen Sie sicher, dass Sie {area, null} zum Wörterbuch hinzufügen (oder wie auch immer Ihr Bereich heißt), wenn Sie MVC 2 und höher verwenden - sonst wird es von der Seite geerbt, die Sie besuchen
wollten
2

Meine Lösung für dieses Problem war eine benutzerdefinierte ActionResultKlasse:

    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; }
    }
Kieron
quelle
0

Dennoch, wenn man die integrierten ASP.NET FormsAuthentication zu verwenden entscheidet, kann man overide Application_AuthenticateRequestin Global.asax.cswie folgt:

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;
        }
    }
}
Turdus-Merula
quelle