2011-08-18 31 views
8

Estoy recién aprendiendo ASP.NET MVC, y recientemente encontré el atributo [RequireHttps] para redirigir automáticamente una solicitud GET para usar SSL, como ...Cómo especificar un puerto diferente con el atributo RequireHttps en MVC3

[RequireHttps] //apply to all actions in controller 
public class SomeController 
{ 
    [RequireHttps] //apply to this action only 
    public ActionResult SomeAction() 
    { 
     ... 
    } 
} 

al utilizar IIS Express como el servidor de desarrollo, esto vuelve a dirigir correctamente la solicitud de http://localhost:55945/... a https://localhost/....

Sin embargo, en mi sistema de desarrollo, mi proyecto está utilizando HTTPS en el puerto 44300 (esto fue configurado automáticamente por Visual Studio 2010), y todavía no he encontrado ninguna manera de decirle a MVC que use ese número de puerto en la redirección, entonces que va al https://localhost:43300/... requerido en su lugar.

Realmente esperaba que esto sea automático, dado que el número de puerto SSL fue establecido automáticamente por Visual Studio, y hasta donde puedo decir, esto debe afectar a todos los desarrolladores que usan el atributo [RequireHttps] en MVC3. En mi búsqueda de una solución, he visto algunas soluciones irregulares de "solución alternativa", pero nada que parezca definitivamente "la forma correcta" de solucionarlo.

Haciendo las cosas "de la manera correcta", qué cambio (ya sea en el código fuente o en la configuración de mi proyecto) para decirle al atributo [RequireHttps] en MVC3 que use el puerto HTTPS que mi proyecto está configurado estar usando?

O, como alternativa, ¿hay alguna otra "manera correcta" completamente diferente y mejor para configurar el soporte SSL en un proyecto MVC3, que no tiene este problema?

Respuesta

12

El RequireHttpsAttribute es bastante simple y no se puede parametrizar para redirigir a un puerto específico. Si realmente lo necesita, puede crear una subclase y anular el método HandleNonHttpsRequest para componer la url de redirección de manera diferente.

protected override void HandleNonHttpsRequest(AuthorizationContext filterContext) { 
    base.HandleNonHttpsRequest(filterContext); 

    // redirect to HTTPS version of page 
    string url = "https://" + filterContext.HttpContext.Request.Url.Host + ":" + MyConfig.SslPort + filterContext.HttpContext.Request.RawUrl; 
    filterContext.Result = new RedirectResult(url); 
} 

Sin embargo, si todo el sitio funciona con HTTPS sólo podría configurar en VS en las propiedades del proyecto Web (Web -> Acción de inicio -> URL de inicio) para abrir la URL HTTPS correcta con su puerto y no utilizar la función de redireccionamiento para pruebas locales.

+0

Obtengo la primera parte de tu respuesta, aunque parezca un truco, para algo que no debería ser pirateado. Pero no entiendo tu último párrafo. Necesito que mi sistema de desarrollo se comporte (tan de cerca como sea posible) de la misma manera que se comportará el sistema de los usuarios. Eso significa que algunas páginas usarán HTTPS, y otras no. También significa que no puedo controlar con qué URL ingresará el usuario. –

+0

Lo que quiero decir es que probablemente no necesite la redirección real para trabajar en su máquina de desarrollo, y en producción será de todos modos el puerto HTTPS estándar. Este sería el caso si todo el sitio se ejecutara bajo HTTPS, porque podría simplemente iniciar la aplicación con el puerto HTTPS correcto y mantenerse dentro del contexto HTTPS. Si entiendo bien, este no es el caso, por lo que debe hacer que la redirección también funcione en su máquina de desarrollo. Actualizo la respuesta y trato de expresar mejor lo que quise decir, pero puede que no sea relevante para ti de todos modos. –

2

Algunas cosas que pueden ser útiles.

hay una versión del código fuente de RequireHttpsAttribute en este hilo: Where is the source for RequireHttpsAttribute?

También hay una clase casi idéntico llamado RequireSslAttribute en CodePlex mencionado en el mismo hilo. http://aspnet.codeplex.com/SourceControl/changeset/view/63930#391756

Aquí hay un ejemplo de un atributo que se puede utilizar para alternar entre http a https o al revés en función de la propiedad TargetUriScheme. También incluye propiedades para especificar números de puerto.

He elegido utilizar un bloque #if DEBUG en mi constructor para establecer mis puertos de desarrollo locales cuando estoy compilando bajo la configuración de depuración.Esto funciona para mí, ya que siempre compilo bajo versión al momento de la implementación, en cuyo caso los números de puerto serán nulos por defecto y quedarán fuera de la url.

Los números de puerto también se pueden establecer al aplicar el atributo a un método de acción. También podría ver enganchándolos a un archivo de configuración u otra fuente de configuración para determinar los números de puerto en tiempo de ejecución.

public class ToggleHttpHttpsAttribute : FilterAttribute, IAuthorizationFilter 
{ 
    //supported uri scheme values 
    public enum UriScheme 
    { 
     Http, 
     Https 
    } 

    public ToggleHttpHttpsAttribute(
     UriScheme uriScheme = UriScheme.Http) 
    { 
     TargetUriScheme = uriScheme; 
    #if DEBUG 
     //set DEBUG ports 
     HttpPort = 55892; 
     HttpsPort = 44301; 
    #endif 
    } 

    private UriScheme TargetUriScheme { get; set; } 
    public int? HttpPort { get; set; } 
    public int? HttpsPort { get; set; } 

    public void OnAuthorization(AuthorizationContext filterContext) 
    { 
     if(filterContext == null) 
     { 
      throw new ArgumentNullException("filterContext"); 
     } 

     bool isHttps = filterContext.HttpContext.Request.IsSecureConnection; 

     if ((isHttps && TargetUriScheme == UriScheme.Http) || (!isHttps && TargetUriScheme == UriScheme.Https)) 
     { 
      ToggleUriScheme(filterContext); 
     } 
    } 

    private void ToggleUriScheme(AuthorizationContext filterContext) 
    { 
     //only allow toggle if GET request 
     if (!string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) 
     { 
      throw new InvalidOperationException("ToggleHttpHttpsAttribute can only be used on GET requests."); 
     } 

     filterContext.Result = GetRedirectResult(filterContext); 
    } 

    private RedirectResult GetRedirectResult(AuthorizationContext filterContext) 
    { 
     string prefix = TargetUriScheme == UriScheme.Http 
      ? "http://" 
      : "https://"; 

     int? port = TargetUriScheme == UriScheme.Http 
      ? HttpPort 
      : HttpsPort; 

     string url = string.Format(
      "{0}{1}{2}{3}", 
      prefix, 
      filterContext.HttpContext.Request.Url.Host, 
      port == null ? "" : string.Format(":{0}", port), 
      filterContext.HttpContext.Request.RawUrl); 

     return new RedirectResult(url); 
    } 
} 
2

que tienen este mismo problema, y ​​estoy resolverlo utilizando tanto la regla RequireHttpsAttribute y una reescritura de URL en Web.config. La regla de reescritura coincide con los números de puerto no estándar y se ejecuta antes del atributo. Puede usar una transformación Web.config para eliminar la regla de reescritura en la implementación, pero si la deja en ella no debería tener ningún efecto. En producción usará el número de puerto estándar que la regla no coincidirá. Entonces el atributo lo atrapará.

Aquí está la regla:

<system.webServer> 
    <rewrite> 
    <rules> 
    <!-- Redirect HTTP requests to HTTPS, using the non-standard development ports. 
    On deployment, this rule is removed, and the RequireHttpAttribute filter globally applied 
    in SlicerWeb.FilterConfig takes over. This rewrite rule executes before the attribute 
    would be applied, and so can apply the port numbers --> 
     <rule name="HTTPS redirect" stopProcessing="true"> 
     <match url="(.*)" /> 
     <conditions> 
      <add input="{HTTPS}" pattern="off" ignoreCase="true" /> 
      <add input="{SERVER_PORT}" pattern="60470" /> 
     </conditions> 
     <action type="Redirect" url="https://{SERVER_NAME}:44300/{R:1}" redirectType="Found" /> 
     </rule> 
    </rules> 
    </rewrite> 
</system.webServer> 

La desventaja de este enfoque es que no está confiando en el atributo cuando se ejecuta localmente. Pero si lo ha aplicado una vez de forma global, en lugar de agregarlo a cada controlador, eso está bien en mi opinión.

Cuestiones relacionadas