tag net mvc component asp asp.net asp.net-mvc ssl https

asp.net - mvc - forms asp net core



Páginas SSL bajo ASP.NET MVC (11)

¿Cómo hago para usar HTTPS en algunas de las páginas de mi sitio ASP.NET MVC?

Steve Sanderson tiene un buen tutorial sobre cómo hacer esto de manera SECA en la Vista previa 4 en:

http://blog.codeville.net/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/

¿Hay alguna manera mejor / actualizada con Preview 5?






Como escribió Amadiere , [RequireHttps] funciona muy bien en MVC 2 para ingresar a HTTPS. Pero si solo quieres usar HTTPS para algunas páginas como dijiste, MVC 2 no te quiere: una vez que cambia a un usuario a HTTPS, permanece bloqueado hasta que lo redirigas manualmente.

El enfoque que utilicé es usar otro atributo personalizado, [ExitHttpsIfNotRequired]. Cuando se adjunta a un controlador o acción, esto redirigirá a HTTP si:

  1. La solicitud fue HTTPS
  2. El atributo [RequireHttps] no se aplicó a la acción (o controlador)
  3. La solicitud era un GET (redirigir un POST conduciría a todo tipo de problemas).

Es un poco demasiado grande para publicar aquí, pero puedes ver el código aquí más algunos detalles adicionales.



Me crucé con esta pregunta y espero que mi solución pueda ayudar a alguien.

Tenemos algunos problemas: - Necesitamos asegurar acciones específicas, por ejemplo, "Iniciar sesión" en "Cuenta". Podemos usar la compilación en el atributo RequireHttps, que es genial, pero nos redireccionará de nuevo con https: //. - Deberíamos hacer que nuestros enlaces, formularios y "SSL estén al tanto".

En general, mi solución permite especificar rutas que usarán url absoluta, además de la capacidad de especificar el protocolo. Puede usar esta aproximación para especificar el protocolo "https".

Entonces, primero he creado una enumeración ConnectionProtocol:

/// <summary> /// Enum representing the available secure connection requirements /// </summary> public enum ConnectionProtocol { /// <summary> /// No secure connection requirement /// </summary> Ignore, /// <summary> /// No secure connection should be used, use standard http request. /// </summary> Http, /// <summary> /// The connection should be secured using SSL (https protocol). /// </summary> Https }

Ahora, he creado una versión enrollada a mano de RequireSsl. Modifiqué el código fuente RequireSsl original para permitir la redirección a http: // urls. Además, he puesto un campo que nos permite determinar si debemos requerir SSL o no (lo estoy usando con el preprocesador DEBUG).

/* Note: * This is hand-rolled version of the original System.Web.Mvc.RequireHttpsAttribute. * This version contains three improvements: * - Allows to redirect back into http:// addresses, based on the <see cref="SecureConnectionRequirement" /> Requirement property. * - Allows to turn the protocol scheme redirection off based on given condition. * - Using Request.IsCurrentConnectionSecured() extension method, which contains fix for load-balanced servers. */ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public sealed class RequireHttpsAttribute : FilterAttribute, IAuthorizationFilter { public RequireHttpsAttribute() { Protocol = ConnectionProtocol.Ignore; } /// <summary> /// Gets or sets the secure connection required protocol scheme level /// </summary> public ConnectionProtocol Protocol { get; set; } /// <summary> /// Gets the value that indicates if secure connections are been allowed /// </summary> public bool SecureConnectionsAllowed { get { #if DEBUG return false; #else return true; #endif } } public void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext) { if (filterContext == null) { throw new ArgumentNullException("filterContext"); } /* Are we allowed to use secure connections? */ if (!SecureConnectionsAllowed) return; switch (Protocol) { case ConnectionProtocol.Https: if (!filterContext.HttpContext.Request.IsCurrentConnectionSecured()) { HandleNonHttpsRequest(filterContext); } break; case ConnectionProtocol.Http: if (filterContext.HttpContext.Request.IsCurrentConnectionSecured()) { HandleNonHttpRequest(filterContext); } break; } } private void HandleNonHttpsRequest(AuthorizationContext filterContext) { // only redirect for GET requests, otherwise the browser might not propagate the verb and request // body correctly. if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("The requested resource can only be accessed via SSL."); } // redirect to HTTPS version of page string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl; filterContext.Result = new RedirectResult(url); } private void HandleNonHttpRequest(AuthorizationContext filterContext) { if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("The requested resource can only be accessed without SSL."); } // redirect to HTTP version of page string url = "http://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl; filterContext.Result = new RedirectResult(url); } }

Ahora, este RequireSsl hará la siguiente base en su valor de atributo de requisitos: - Ignorar: no hará nada. - Http: forzará la redirección al protocolo http. - Https: forzará la redirección al protocolo https.

Debe crear su propio controlador base y establecer este atributo en Http.

[RequireSsl(Requirement = ConnectionProtocol.Http)] public class MyController : Controller { public MyController() { } }

Ahora, en cada cpntroller / acción que desea requerir SSL, simplemente configure este atributo con ConnectionProtocol.Https.

Ahora pasemos a las URL: tuvimos pocos problemas con el motor de enrutamiento de URL. Puede leer más sobre ellos en http://blog.stevensanderson.com/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/ . La solución sugerida en este post es teóricamente buena, pero vieja y no me gusta el enfoque.

Mi solución es la siguiente: crear una subclase de la clase básica "Ruta":

clase pública AbsoluteUrlRoute: Route {#region ctor

/// <summary> /// Initializes a new instance of the System.Web.Routing.Route class, by using /// the specified URL pattern and handler class. /// </summary> /// <param name="url">The URL pattern for the route.</param> /// <param name="routeHandler">The object that processes requests for the route.</param> public AbsoluteUrlRoute(string url, IRouteHandler routeHandler) : base(url, routeHandler) { } /// <summary> /// Initializes a new instance of the System.Web.Routing.Route class, by using /// the specified URL pattern and handler class. /// </summary> /// <param name="url">The URL pattern for the route.</param> /// <param name="defaults">The values to use for any parameters that are missing in the URL.</param> /// <param name="routeHandler">The object that processes requests for the route.</param> public AbsoluteUrlRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler) : base(url, defaults, routeHandler) { } /// <summary> /// Initializes a new instance of the System.Web.Routing.Route class, by using /// the specified URL pattern and handler class. /// </summary> /// <param name="url">The URL pattern for the route.</param> /// <param name="defaults">The values to use for any parameters that are missing in the URL.</param> /// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param> /// <param name="routeHandler">The object that processes requests for the route.</param> public AbsoluteUrlRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler) : base(url, defaults, constraints, routeHandler) { } /// <summary> /// Initializes a new instance of the System.Web.Routing.Route class, by using /// the specified URL pattern and handler class. /// </summary> /// <param name="url">The URL pattern for the route.</param> /// <param name="defaults">The values to use for any parameters that are missing in the URL.</param> /// <param name="constraints">A regular expression that specifies valid values for a URL parameter.</param> /// <param name="dataTokens">Custom values that are passed to the route handler, but which are not used /// to determine whether the route matches a specific URL pattern. These values /// are passed to the route handler, where they can be used for processing the /// request.</param> /// <param name="routeHandler">The object that processes requests for the route.</param> public AbsoluteUrlRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler) : base(url, defaults, constraints, dataTokens, routeHandler) { } #endregion public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { var virtualPath = base.GetVirtualPath(requestContext, values); if (virtualPath != null) { var scheme = "http"; if (this.DataTokens != null && (string)this.DataTokens["scheme"] != string.Empty) { scheme = (string) this.DataTokens["scheme"]; } virtualPath.VirtualPath = MakeAbsoluteUrl(requestContext, virtualPath.VirtualPath, scheme); return virtualPath; } return null; } #region Helpers /// <summary> /// Creates an absolute url /// </summary> /// <param name="requestContext">The request context</param> /// <param name="virtualPath">The initial virtual relative path</param> /// <param name="scheme">The protocol scheme</param> /// <returns>The absolute URL</returns> private string MakeAbsoluteUrl(RequestContext requestContext, string virtualPath, string scheme) { return string.Format("{0}://{1}{2}{3}{4}", scheme, requestContext.HttpContext.Request.Url.Host, requestContext.HttpContext.Request.ApplicationPath, requestContext.HttpContext.Request.ApplicationPath.EndsWith("/") ? "" : "/", virtualPath); } #endregion }

Esta versión de la clase "Ruta" creará url absoluta. El truco aquí, seguido de la sugerencia del autor de la publicación de blog, es utilizar DataToken para especificar el esquema (ejemplo al final :)).

Ahora, si generamos una URL, por ejemplo, para la ruta "Cuenta / LogOn", obtendremos "/ http://example.com/Account/LogOn ", ya que el UrlRoutingModule considera que todas las URL son relativas. Podemos arreglar eso usando HttpModule personalizado:

public class AbsoluteUrlRoutingModule : UrlRoutingModule { protected override void Init(System.Web.HttpApplication application) { application.PostMapRequestHandler += application_PostMapRequestHandler; base.Init(application); } protected void application_PostMapRequestHandler(object sender, EventArgs e) { var wrapper = new AbsoluteUrlAwareHttpContextWrapper(((HttpApplication)sender).Context); } public override void PostResolveRequestCache(HttpContextBase context) { base.PostResolveRequestCache(new AbsoluteUrlAwareHttpContextWrapper(HttpContext.Current)); } private class AbsoluteUrlAwareHttpContextWrapper : HttpContextWrapper { private readonly HttpContext _context; private HttpResponseBase _response = null; public AbsoluteUrlAwareHttpContextWrapper(HttpContext context) : base(context) { this._context = context; } public override HttpResponseBase Response { get { return _response ?? (_response = new AbsoluteUrlAwareHttpResponseWrapper(_context.Response)); } } private class AbsoluteUrlAwareHttpResponseWrapper : HttpResponseWrapper { public AbsoluteUrlAwareHttpResponseWrapper(HttpResponse response) : base(response) { } public override string ApplyAppPathModifier(string virtualPath) { int length = virtualPath.Length; if (length > 7 && virtualPath.Substring(0, 7) == "/http:/") return virtualPath.Substring(1); else if (length > 8 && virtualPath.Substring(0, 8) == "/https:/") return virtualPath.Substring(1); return base.ApplyAppPathModifier(virtualPath); } } } }

Como este módulo está anulando la implementación básica de UrlRoutingModule, deberíamos eliminar el httpModule base y registrar el nuestro en web.config. Entonces, bajo "system.web" establece:

<httpModules> <!-- Removing the default UrlRoutingModule and inserting our own absolute url routing module --> <remove name="UrlRoutingModule-4.0" /> <add name="UrlRoutingModule-4.0" type="MyApp.Web.Mvc.Routing.AbsoluteUrlRoutingModule" /> </httpModules>

Eso es :).

Para registrar una ruta absoluta / protocolo seguida, debe hacer:

routes.Add(new AbsoluteUrlRoute("Account/LogOn", new MvcRouteHandler()) { Defaults = new RouteValueDictionary(new {controller = "Account", action = "LogOn", area = ""}), DataTokens = new RouteValueDictionary(new {scheme = "https"}) });

Me encantará escuchar tus comentarios + mejoras. Espero que pueda ayudar! :)

Editar: Olvidé incluir el método de extensión IsCurrentConnectionSecured () (demasiados fragmentos: P). Este es un método de extensión que generalmente usa Request.IsSecuredConnection. Sin embargo, este enfoque no funcionará cuando se utilice el equilibrio de carga, por lo que este método puede eludir esto (tomado de nopCommerce).

/// <summary> /// Gets a value indicating whether current connection is secured /// </summary> /// <param name="request">The base request context</param> /// <returns>true - secured, false - not secured</returns> /// <remarks><![CDATA[ This method checks whether or not the connection is secured. /// There''s a standard Request.IsSecureConnection attribute, but it won''t be loaded correctly in case of load-balancer. /// See: <a href="http://nopcommerce.codeplex.com/SourceControl/changeset/view/16de4a113aa9#src/Libraries/Nop.Core/WebHelper.cs">nopCommerce WebHelper IsCurrentConnectionSecured()</a>]]></remarks> public static bool IsCurrentConnectionSecured(this HttpRequestBase request) { return request != null && request.IsSecureConnection; // when your hosting uses a load balancer on their server then the Request.IsSecureConnection is never got set to true, use the statement below // just uncomment it //return request != null && request.ServerVariables["HTTP_CLUSTER_HTTPS"] == "on"; }


Para aquellos que no son fanáticos de los enfoques de desarrollo orientados a atributos, aquí hay una pieza de código que podría ayudar:

public static readonly string[] SecurePages = new[] { "login", "join" }; protected void Application_AuthorizeRequest(object sender, EventArgs e) { var pageName = RequestHelper.GetPageNameOrDefault(); if (!HttpContext.Current.Request.IsSecureConnection && (HttpContext.Current.Request.IsAuthenticated || SecurePages.Contains(pageName))) { Response.Redirect("https://" + Request.ServerVariables["HTTP_HOST"] + HttpContext.Current.Request.RawUrl); } if (HttpContext.Current.Request.IsSecureConnection && !HttpContext.Current.Request.IsAuthenticated && !SecurePages.Contains(pageName)) { Response.Redirect("http://" + Request.ServerVariables["HTTP_HOST"] + HttpContext.Current.Request.RawUrl); } }

Hay varias razones para evitar los atributos y una de ellas es que si desea ver la lista de todas las páginas seguras, tendrá que saltar sobre todos los controladores en la solución.



MVCFutures tiene un atributo ''RequireSSL''.

(gracias a Adam por señalar eso en su blog actualizado actualizado)

Simplemente aplíquelo a su método de acción, con ''Redirect = true'' si desea una solicitud http: // para convertirse automáticamente en https: //:

[RequireSsl(Redirect = true)]

Ver también: ASP.NET MVC RequireHttps solo en producción


MVC 6 (ASP.NET Core 1.0) funciona ligeramente diferente con Startup.cs.

Para usar RequireHttpsAttribute (como se menciona en la answer de Amadiere) en todas las páginas, puede agregar esto en Startup.cs en lugar de usar el estilo de atributo en cada controlador (o en lugar de crear un BaseController del que heredarán todos sus controladores).

Startup.cs - filtro de registro:

public void ConfigureServices(IServiceCollection services) { // TODO: Register other services services.AddMvc(options => { options.Filters.Add(typeof(RequireHttpsAttribute)); }); }

Para obtener más información sobre las decisiones de diseño para el enfoque anterior, vea mi respuesta a una pregunta similar sobre cómo excluir que las solicitudes de localhost sean manejadas por RequireHttpsAttribute .