2008-10-01 16 views
79

Jak korzystać z HTTPS dla niektórych stron w mojej witrynie opartej na ASP.NET MVC?Strony SSL pod ASP.NET MVC

Steve Sanderson jest całkiem dobry tutorial jak to zrobić w suchym sposób na podglądzie na 4:

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

Czy istnieje lepszy/zaktualizowany sposób z podglądu 5 ?,

+3

To jest bardzo stary. W przypadku MVC4 i wyższych zobacz mój blog http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-new -allowanonymous-attribute.aspx – RickAndMSFT

Odpowiedz

93

Jeśli używasz ASP.NET MVC 2 Preview 2 or higher, można teraz po prostu użyć:

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

chociaż parametr zamówienie Warto zauważyć, jak mentioned here.

+23

Możesz to również zrobić na poziomie kontrolera. Co więcej, jeśli chcesz, aby cała aplikacja była protokołem SSL, możesz utworzyć kontroler podstawowy, rozszerzyć go dla wszystkich kontrolerów i zastosować tam atrybut. – ashes999

+22

Alternatywnie możesz dodać, że jest to globalny filtr MVC3 w Global.asax GlobalFilters.Filters.Add (new RequireHttpsAttribute()); – GraemeMiller

+2

Nikt nie gwarantuje, że inni programiści będą używać Twojego kontrolera pochodnego. Możesz wykonać jedno połączenie, aby wymusić HTTPS - zobacz mój blog http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and -the-new-allowanonymous-attribute.aspx – RickAndMSFT

1

Oto blog post by Adam Salvo, który używa ActionFilter.

+0

upewnij się, że zobaczysz następujący wpis, który sam napisał: http://blog.salvoz.com/2009/04/25/PartialSSLAndAuthorizationWithAspNetMVCRevisited.aspx –

17

MVCFutures ma atrybut "RequireSSL".

(dzięki Adam za pointing that out w zaktualizowanym blogpost)

Wystarczy zastosować go do swojego sposobu działania, z 'redirect = true' jeśli chcesz http: // żądania automatycznie stają się od https: //:

[RequireSsl(Redirect = true)] 

Zobacz także: ASP.NET MVC RequireHttps in Production Only

+0

Czy muszę go podklasować, aby obsłużyć żądania localhost ? –

+0

jednym ze sposobów jest utworzenie certyfikatu dla lokalnej maszyny i użycie go. Myślę, że całkowicie wyłączyć go dla localhost rzeczywiście trzeba podklasy lub duplikat kodu. nie wiesz, co to zalecana metoda jest –

+1

Wygląda na to, że jest zapieczętowana, więc muszę wprowadzić kod. Bummer. Certyfikat dla komputera lokalnego działa tylko w usługach IIS, ale nie na serwerze WWW. –

8

Oto ostatni post z Dan Wahlin na to:

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

Używa atrybutu ActionFilter.

+2

To wydaje się być najlepszym sposobem w tej chwili. – royco

+0

+1 rok później, ponieważ rozmowa isLocal pomogła mi rozwiązać problem, który stawał się prawdziwym bólem na @@@ – heisenberg

+1

Powyższe ma datę, Dla MVC4 i wyżej, zobacz mój blog http://blogs.msdn.com /b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-new-allowanonymous-attribute.aspx – RickAndMSFT

9

Jako Amadiere wrote [RequireHttps] działa świetnie w MVC 2 dla wprowadzając HTTPS. Ale jeśli chcesz tylko używać HTTPS do niektórych stron, jak powiedziałeś, MVC 2 nie da ci żadnej miłości - po przełączeniu użytkownika na HTTPS utkną tam, dopóki nie przekierujesz go ręcznie.

Podejście, którego użyłem, to użycie innego niestandardowego atrybutu, [ExitHttpsIfNotRequired]. Kiedy dołączone do kontrolera lub działania będzie przekierowana na HTTP, jeżeli:

  1. Żądanie to HTTPS
  2. Sieć [RequireHttps] Żądanie atrybut nie był stosowany do akcji (lub sterownika)
  3. był GET (przekierowanie POST może prowadzić do różnego rodzaju problemów).

Jest nieco za duży, aby opublikować tutaj, ale można zobaczyć the code here oraz kilka dodatkowych informacji.

+0

Poprawka AllowAnonymous to. W przypadku MVC4 i wyższych zobacz mój blog http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-new -allowanonymous-attribute.aspx – RickAndMSFT

3

Dla tych, którzy nie są fanem atrybutów zorientowanych metod rozwojowych, o to fragment kodu, który mógłby pomóc:

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

Istnieje kilka powodów, dla których trzeba unikać atrybuty i jeden z nich jest, jeśli chcesz aby przejrzeć listę wszystkich zabezpieczonych stron, będziesz musiał przeskoczyć przez wszystkie kontrolery w rozwiązaniu.

+0

Myślę, że większość ludzi nie zgodziłaby się z tobą w tej sprawie, chociaż zapewnienie alternatywnej metody jest zawsze przydatne ... –

2

Poszedłem na to pytanie i mam nadzieję, że moje rozwiązanie może pomóc komuś.

Mamy kilka problemów: - Musimy zabezpieczyć określone działania, na przykład "LogOn" w "Konto". Możemy użyć atrybutu build in RequireHttps, co jest świetne - ale przekieruje nas z powrotem za pomocą https: //. - Powinniśmy tworzyć nasze łącza, formularze i takie "SSL-aware".

Ogólnie rzecz biorąc, moje rozwiązanie pozwala określić trasy, które będą używać bezwzględnego adresu URL, oprócz możliwości określenia protokołu. Możesz użyć tej aplikacji, aby określić protokół "https".

Więc, po pierwsze Utworzyłem 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 
} 

Teraz mam utworzony ręcznie walcowane wersję requiressl. Zmodyfikowałem oryginalny kod źródłowy RequireSsl, aby umożliwić przekierowanie z powrotem do http: // urls. Ponadto umieściłem pole, które pozwala nam określić, czy powinniśmy wymagać SSL, czy też nie (używam go z pre-procesorem DEBUG). nie zrobi nic: - ignorowanie :

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

Teraz to requiressl zrobi następujące bazy od wymagań wartość atrybutu. - Http: Wymusi przekierowanie na protokół http. - Https: Wymusi przekierowanie na protokół https.

Powinieneś stworzyć swój własny kontroler bazowy i ustawić ten atrybut na Http.

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

Teraz, w każdym cpntroller/działania Państwo chcieliby wymagać SSL - wystarczy ustawić ten atrybut z ConnectionProtocol.Https.

Teraz przenosimy się na adresy URL: mamy niewiele problemów z mechanizmem routingu adresów URL. Więcej o nich można przeczytać pod numerem http://blog.stevensanderson.com/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/. Rozwiązanie sugerowane w tym poście jest teoretycznie dobre, ale stare i nie podoba mi się aplikacja.

moje rozwiązania jest następująca: utworzyć podklasę klasy podstawowej "trasa":

public class AbsoluteUrlRoute Route { #region konstruktor

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

Ta wersja "Szlaku" klasa utworzy bezwzględny adres URL.Sztuczka tutaj, a następnie sugestia autora postu na blogu, polega na użyciu DataToken do określenia schematu (przykład na końcu :)).

Teraz, jeśli wygenerujemy URL, na przykład dla trasy "Konto/LogOn" otrzymamy "/ http://example.com/Account/LogOn" - to dlatego, że UrlRoutingModule widzi wszystkie adresy URL jako względne. Możemy naprawić przy użyciu niestandardowych 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); 
      } 
     } 
    } 
} 

Ponieważ moduł ten jest nadrzędny realizację podstawy UrlRoutingModule, powinniśmy usunąć bazę protokołu HTTP i zarejestrować się w nasze web.config. Tak więc w zestawie "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> 

To jest :).

W celu zarejestrowania bezwzględnej/protokół następnie trasą, należy zrobić:

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

Will usłyszeć swoją opinię + ulepszeń. Mam nadzieję, że to pomoże! :)

Edytuj: Zapomniałem włączyć metodę rozszerzenia IsCurrentConnectionSecured() (zbyt wiele fragmentów: P). Jest to metoda rozszerzenia, która zazwyczaj używa Request.IsSecuredConnection. Jednak ta aplikacja nie będzie działała przy użyciu równoważenia obciążenia - więc ta metoda może ominąć to (wzięte z 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"; 
    } 
0

MVC 6 (ASP.NET Rdzeń 1,0) pracuje nieco różne w Startup.cs.

Aby użyć RequireHttpsAttribute (jak wspomniano w answer przez Amadiere) na wszystkich stronach, można dodać to w Startup.cs zamiast używać stylu atrybutu na każdym kontrolerze (lub zamiast tworzyć kontroler BaseController do dziedziczenia wszystkich kontrolerów) .

Startup.cs - zarejestruj filtr:

public void ConfigureServices(IServiceCollection services) 
{ 
    // TODO: Register other services 

    services.AddMvc(options => 
    { 
     options.Filters.Add(typeof(RequireHttpsAttribute)); 
    }); 
} 

Aby uzyskać więcej informacji na temat decyzji projektowych dla powyższego podejścia, zobacz moją odpowiedź na podobne pytanie o how to exclude localhost requests from being handled by the RequireHttpsAttribute.

Powiązane problemy