2010-11-25 14 views
8

Así que he encontrado el atributo [RequiresHttps] pero una vez que está en https su tipo de pegado allí, para intentar y poder tener acciones en una sola url (y esquema) He descubierto que he tenido que crear mi propio ExtendedController para volver a http para las acciones que no usan [RequireHttps].Moverse entre HTTP y HTTPS en ASP.NET MVC

¿Me pregunto si lo que estoy haciendo está bien o si hay una manera mejor?

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

Una cosa que acabo de darme cuenta es que también debería verificar en filterContext.IsChildAction - Me pregunto si existe la posibilidad de encontrar los mismos problemas al usar RequiresHttp en las acciones secundarias. Parece que se necesita una solución más completa tal vez en el lado de enrutamiento en lugar de en los controladores. – Mark

Respuesta

11

lo que tienes es syntatically correcta, sin embargo, una sugerencia es crear un nuevo filtro de Acción que hereda de la RequireHttpsAttribute defecto y toma un parámetro para cambiar entre HTTP y 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); 
     } 
    } 
} 

Luego, en su método de acción o controlador se debería utilizar:

[RequireHttps (RequireSecure = true)] 

...

o

[RequireHttps (RequireSecure = false)] 
+0

Una cosa a tener en cuenta es que esta opción no es segura "public bool RequireSecure = false;" mientras que System.Web.Mvc.RequireHttpsAttribute requiere HTTPS. Si quiere hacer un paralelo con System.Web.Mvc.RequireHttpsAttribute, simplemente establezca el valor predeterminado en "true". – ChrisP

1

Para que sea poco más manejable. Esta solución asume que la mayoría de su aplicación web usa el esquema HTTP.

  1. Crear nueva RequiresHttp filtro de acción (utilizar HTTP si el atributo NeedSsl no se aplica de manera explícita en la acción o controlador),

    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. Y nuevo atributo en blanco NeedSSL (para fines de indicación)

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 
    public sealed class NeedSslAttribute : Attribute { } 
    
  3. Aplicar RequiresHttp como filtro de acción global en Global.aspx.cs

    public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
    { 
        filters.Add(new RequiresHttp()); 
    } 
    
  4. Ahora Aplicar NeedSslAttribute en los controladores y acciones lugar en el que desea utilizar HTTPS esquema

    [NeedSsl] 
    [AllowAnonymous] 
    public ActionResult LogOn() 
    

Este código no es perfecto como filtro de acción RequiresHttp qué múltiples puestos de trabajo, es decir, comprobar NeedSsl atributo y aplicar HTTP o HTTPS esquema. Hubiera sido mejor si pudiéramos usar dos filtros de acción RequiresHTTP y RequiresHTTPS.

Ahora si RequiresHTTP se estableció como filtro global y el filtro RequiresHTTPS se aplicó en acciones específicas y el filtro RequiresHTTPS específico habría dado preferencia.