91

Je crée un site Web multiclient qui héberge des pages pour les clients. Le premier segment de l'URL sera une chaîne qui identifie le client, défini dans Global.asax en utilisant le schéma de routage URL suivante:Comment rediriger vers une URL de connexion dynamique dans ASP.NET MVC

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

Cela fonctionne très bien, avec des URL telles que/foo/Accueil/Index. Cependant, lorsque j'utilise l'attribut [Autoriser], je souhaite rediriger vers une page de connexion qui utilise également le même schéma de mappage. Donc, si le client est foo, la page de connexion serait/foo/Account/Login au lieu de la redirection Fixed/Account/Login définie dans web.config. MVC utilise un HttpUnauthorizedResult pour retourner un statut non autorisé 401, ce qui, je présume, amène ASP.NET à rediriger vers la page définie dans web.config

Alors, est-ce que quelqu'un sait comment contourner le comportement de redirection de connexion ASP.NET? Ou serait-il préférable de rediriger dans MVC en créant un attribut d'autorisation personnalisé?

EDIT - Réponse: après des fouilles dans la source .Net, j'ai décidé qu'un attribut d'authentification personnalisé est la meilleure solution:

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

faire presque exactement la même chose avec le routage, donc je besoin de ce! Merci! –

+0

Merci, j'essayais de comprendre comment faire quelque chose de similaire. – Chance

+0

cela m'a donné une idée pour la mise en œuvre propre, merci beaucoup! –

Répondre

30

Je pense que le principal problème est que si vous allez pour se greffer sur la classe ASP.NET FormsAuthentication intégrée (et il n'y a pas de bonne raison pour laquelle vous ne devriez pas), quelque chose à la fin de la journée va appeler FormsAuthentication.RedirectToLoginPage() qui va regarder l'URL configuré. Il n'y a jamais qu'une seule URL de connexion, et c'est ainsi qu'ils l'ont conçu. Mon coup au problème (peut-être une implémentation de Rube Goldberg) serait de le laisser rediriger vers une seule page de connexion à la racine partagée par tous les clients, disons/compte/login. Cette page de connexion n'afficherait rien; il inspecte le paramètre ReturnUrl ou une valeur que j'ai dans la session ou un cookie qui identifie le client et l'utilise pour émettre une redirection 302 immédiate vers la page spécifique/client/compte/connexion. C'est une redirection supplémentaire, mais probablement pas perceptible et cela vous permet d'utiliser les mécanismes de redirection intégrés.

L'autre option consiste à créer votre propre attribut personnalisé tout en décrivant et en évitant tout ce qui appelle la méthode RedirectToLoginPage() sur la classe FormsAuthentication, puisque vous la remplacerez par votre propre logique de redirection. (Vous pouvez créer votre propre classe qui est similaire.) Comme c'est une classe statique, je ne connais aucun mécanisme par lequel vous pourriez simplement injecter votre propre interface alternative et l'utiliser magiquement avec l'attribut [Authorize] existant, qui coups, mais people have done similar things before.

Espérons que ça aide!

+0

c'est probablement l'approche la plus sûre. créer votre propre attribut [MyAuthorize] est dangereux. à moins que votre build ne vérifie que les utilisateurs n'utilisent pas l'attribut [Authorize] intégré, vous risquez d'oublier (et d'utiliser vous-même) le mauvais –

40

Dans la version RTM d'ASP.NET MVC, la propriété Cancel est manquante. Ce code fonctionne avec 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 } 
         }); 
      } 
     } 
    } 
} 

Edit: Vous pouvez désactiver l'loginUrl d'authentification des formulaires par défaut dans web.config - quelqu'un en cas oublie que vous avez un attribut personnalisé et utilisations l'attribut [Authorize] intégré par erreur.

Modifiez la valeur dans le site Web.config:

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

Ensuite, faire une méthode d'action « ERREUR » qui enregistre une erreur et redirige l'utilisateur vers la page de connexion le plus générique que vous avez.

+2

Assurez-vous d'ajouter {area, null} au dictionnaire (ou à votre domaine) est appelé) si vous utilisez MVC 2 et plus - ou bien il héritera de la page que vous avez tenté de visiter –

2

Ma solution à ce problème était une classe personnalisée 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; } 
    } 
Questions connexes