Pergunta

Como faço para ir sobre o uso de HTTPS para algumas das páginas do meu site com base ASP.NET MVC?

Steve Sanderson tem um bom tutorial bastante sobre como fazer isso de uma maneira seca em Preview 4 em:

http: // blogue .codeville.net / 2008/08/05 / adicionando-httpsssl-support-se aspnet-mvc roteamento /

Existe uma maneira melhor / atualizado com Preview 5?,

Foi útil?

Solução

Se você estiver usando ASP.NET MVC 2 Preview 2 ou superior , agora você pode simplesmente usar:

[RequireHttps]
public ActionResult Login()
{
   return View();
}

No entanto, o parâmetro de ordem é interessante notar, como mencionado aqui .

Outras dicas

MVCFutures tem um atributo 'RequireSSL'.

(graças Adam para apontando que para fora em seu blogpost atualizados )

Basta aplicá-lo ao seu método de ação, com 'redirect = true' se você quer um http: // solicitação para automaticamente tornar https: //:

    [RequireSsl(Redirect = true)]

Veja também: ASP.NET MVC RequireHttps na produção só

Como Amadiere escreveu , [RequireHttps] funciona muito bem no MVC 2 para entrar HTTPS. Mas se você só quer usar HTTPS para alguns páginas como você disse, MVC 2 não lhe dá qualquer amor - uma vez que muda um usuário para HTTPS que está preso lá até que você redirecioná-los manualmente.

A abordagem que eu usei é usar outro atributo personalizado, [ExitHttpsIfNotRequired]. Quando conectado a um controlador ou ação isto irá redirecionar para HTTP se:

  1. O pedido foi HTTPS
  2. O atributo [RequireHttps] não foi aplicada para a acção (ou controlador)
  3. O pedido foi um GET (redirecionamento de um POST levaria a todos os tipos de problemas).

É um pouco grande demais para postar aqui, mas você pode ver o código aqui mais alguns detalhes adicionais.

Aqui está um post recente de Dan Wahlin sobre isso:

http://weblogs.asp.net/dwahlin/archive/2009/08/25/requiring-ssl-for-asp-net-mvc-controllers.aspx

Ele usa um atributo ActionFilter.

Algumas extensões actionlink: http: // www .squaredroot.com / post / 2008/06/11 / MVC-and-SSL.aspx Ou um atributo de ação do controlador que redireciona para https: // http: // fóruns. asp.net/p/1260198/2358380.aspx#2358380

Para aqueles que não é um fã de desenvolvimento orientado para o atributo se aproxima, aqui é um pedaço de código que poderia ajudar:

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);
    }
}

Existem várias razões para atributos evitar e um deles é se você quiser olhar para a lista de todas as páginas seguras que você terá que saltar por cima de todos os controladores em solução.

Eu fui em frente esta questão e espero que a minha solução pode ajuda a alguém.

Temos alguns problemas: - Precisamos garantir ações específicas, por exemplo "logon" em "Conta". Podemos usar a configuração na atributo RequireHttps, o que é ótimo - mas vai redirecionar-nos de volta com https: //. -. Devemos fazer nossos links, formulários e tal "SSL conscientes"

Geralmente, a minha solução permite especificar rotas que irão utilizar url absoluto, além da capacidade de especificar o protocolo. Você pode usar este approch para especificar o protocolo "https".

Assim, em primeiro lugar, criamos um enum 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
}

Versão enrolado à mão Agora, eu criei de requireSSL. Eu modificado o código fonte requireSSL original para permitir redirecionamento de volta para http: // urls. Além disso, eu coloquei um campo que nos permite determinar se devemos exigir SSL ou não (eu estou usando-o com o DEBUG pré-processador).

/* 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);
    }
}

Agora, este requireSSL fará o seguinte de base no seu valor de atributo Requisitos: - Ignorar: não fará nada. - http: Irá forçar o redirecionamento para http protocolo. - https:. Irá forçar o redirecionamento para https protocolo

Você deve criar o seu próprio controlador de base e definir este atributo para Http.

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

Agora, em cada cpntroller / ação que você gostaria de exigir SSL -. Apenas definir esse atributo com ConnectionProtocol.Https

Agora vamos mudar para URLs: Temos alguns problemas com o motor url roteamento. Você pode ler mais sobre eles em http : //blog.stevensanderson.com/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/ . A solução sugerida neste post é theoreticly bom, mas velho e eu não gosto da approch.

As minhas soluções é a seguinte: Criar uma subclasse da classe básica "Route":

AbsoluteUrlRoute classe pública: 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 versão da classe "Route" irá criar url absoluto. O truque aqui, seguido pela sugestão post autor, é usar o DataToken para especificar o esquema (exemplo no final :)).

Agora, se nós vamos gerar um url, por exemplo, para a rota "Conta / LogOn" nós vamos chegar "/ http://example.com/Account/LogOn " - que é desde o UrlRoutingModule vê todas as urls como relativa. Nós podemos consertar isso usando personalizado HttpModule:

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);
            }
        }
    }
}

Uma vez que este módulo está substituindo a implementação base do UrlRoutingModule, devemos remover a base httpModule e registrar nossa em web.config. Assim, sob set "system.web":

<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>

É isso:.)

Para registrar um / protocolo absoluta rota seguida, você deve fazer:

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

Será que gostam de ouvir suas melhorias de feedback +. Espero que possa ajudar! :)

Edit: I esqueceu de incluir o método IsCurrentConnectionSecured () extensão (muitos trechos: P). Este é um método de extensão que geralmente usa Request.IsSecuredConnection. No entanto, este approch não vai funcionar quando se usa de balanceamento de carga -. Modo que este método pode ignorar isso (tirou 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";
    }

Aqui está uma blog de Pablo M. Cibrano partir de janeiro de 2009, que reúne um par de técnicas, incluindo um HttpModule e extensão métodos.

Aqui está um blog por Adam Salvo que usa um ActionFilter .

Isso não é necessariamente MVC específico, mas esta solução funciona tanto para ASP.NET WebForms e MVC:

http://www.codeproject.com/KB/web-security /WebPageSecurity_v2.aspx

Eu usei isso por vários anos e como a separação de interesses e de gestão através do arquivo web.config.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top