2010-11-25 3 views
8

J'ai donc trouvé l'attribut [RequiresHttps] mais une fois que vous êtes dans https votre type de bloqué ici, donc pour essayer d'avoir des actions sur une seule URL (et schéma J'ai découvert que j'ai dû créer mon propre ExtendedController pour revenir à http pour les actions qui n'utilisent pas [RequireHttps].Déplacement entre HTTP et HTTPS dans ASP.NET MVC

Je me demandais simplement si ce que je faisais était correct ou s'il y avait un meilleur moyen?

public class ExtendedController : Controller 
{ 
    protected virtual void HandleHttpRequest(AuthorizationContext filterContext) 
    { 
     if (!string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) 
     { 
      throw new InvalidOperationException("Cannot post between https and http."); 
     } 
     string url = "http://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl; 
     filterContext.Result = new RedirectResult(url); 
    } 

    protected override void OnAuthorization(AuthorizationContext filterContext) 
    { 
     base.OnAuthorization(filterContext); 
     object[] attributes = filterContext.ActionDescriptor.GetCustomAttributes(true); 
     if (!attributes.Any(a => a is RequireHttpsAttribute)) 
     { 
      if (filterContext == null) 
      { 
       throw new ArgumentNullException("filterContext"); 
      } 
      if (filterContext.HttpContext.Request.IsSecureConnection) 
      { 
       this.HandleHttpRequest(filterContext); 
      } 
     } 
    } 
} 
+0

Une chose que je viens de réaliser est que je devrais aussi vérifier sur filterContext.IsChildAction - Je me demande s'il y a un risque de rencontrer les mêmes problèmes en utilisant RequiresHttp sur les actions enfants. On dirait qu'une solution plus complète est peut-être nécessaire sur le côté de routage au lieu de sur les contrôleurs. – Mark

Répondre

11

Ce que vous avez est cependant une suggestion syntaxiquement correcte, est de créer un nouveau filtre d'action qui hérite de la valeur par défaut RequireHttpsAttribute et prend un paramètre pour basculer entre http et https.

public class RequireHttpsAttribute : System.Web.Mvc.RequireHttpsAttribute 
{ 
    public bool RequireSecure = false; 

    public override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext) 

    { 
     if (RequireSecure) 
     { 
      base.OnAuthorization(filterContext); 
     } 
     else 
     { 
      // non secure requested 
      if (filterContext.HttpContext.Request.IsSecureConnection) 
      { 
       HandleNonHttpRequest(filterContext); 
      } 
     } 
    } 

    protected virtual void HandleNonHttpRequest(AuthorizationContext filterContext) 
    { 
     if (String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) 
     { 
      // redirect to HTTP version of page 
      string url = "http://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl; 
      filterContext.Result = new RedirectResult(url); 
     } 
    } 
} 

Ensuite, sur votre méthode d'action ou un contrôleur que vous utilisez:

[RequireHttps (RequireSecure = true)] 

...

ou

[RequireHttps (RequireSecure = false)] 
+0

Une chose à noter est que cette valeur par défaut est "public bool RequireSecure = false"; tandis que System.Web.Mvc.RequireHttpsAttribute nécessite HTTPS. Si vous souhaitez mettre en parallèle System.Web.Mvc.RequireHttpsAttribute, définissez la valeur par défaut sur "true". – ChrisP

1

Pour le rendre un peu plus facile à gérer. Cette solution suppose que la majorité de votre application Web utilise le schéma HTTP.

  1. Créer un nouveau RequiresHttp filtre d'action (utilisez HTTP si l'attribut NeedSsl est pas applicable explicitement sur l'action ou contrôleur),

    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
        HttpRequestBase req = filterContext.HttpContext.Request; 
        HttpResponseBase res = filterContext.HttpContext.Response; 
    
        bool needSsl = filterContext.ActionDescriptor.IsDefined(typeof(NeedSslAttribute), true) 
            || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(NeedSslAttribute), true); 
    
    
        if (needSsl && !req.IsSecureConnection) //https: secure 
        { 
         var builder = new UriBuilder(req.Url) 
         { 
          Scheme = Uri.UriSchemeHttps, 
          Port = 444 
         }; 
         res.Redirect(builder.Uri.ToString()); 
        } 
        else if (!needSsl && req.IsSecureConnection) //http: non secure 
        { 
         var builder = new UriBuilder(req.Url) 
         { 
          Scheme = Uri.UriSchemeHttp, 
          Port = 8081 
         }; 
         res.Redirect(builder.Uri.ToString()); 
        } 
        base.OnActionExecuting(filterContext); 
    } 
    
  2. Et nouvel attribut vide NeedSSL (à titre d'indication)

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 
    public sealed class NeedSslAttribute : Attribute { } 
    
  3. Appliquer Requis en tant que filtre d'action global dans Global.aspx.cs

    public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
    { 
        filters.Add(new RequiresHttp()); 
    } 
    
  4. Maintenant POSTULEZ postulez NeedSslAttribute sur les contrôleurs et les actions où voulez-vous utiliser système

    [NeedSsl] 
    [AllowAnonymous] 
    public ActionResult LogOn() 
    

HTTPS Ce code n'est pas parfait comme filtre d'action RequiresHttp ne plusieurs emplois ex.chèque NeedSsl attribut et appliquer Schéma HTTP ou HTTPS. Cela aurait été mieux si nous pouvions utiliser deux filtres d'action RequiresHTTP et RequiresHTTPS.

Maintenant, si RequiresHTTP a été défini comme filtre global et RequiresHTTPS filtre a été appliqué sur des actions spécifiques et spécifique RequiresHTTPS aurait donné la préférence.