2008-10-01 68 views
79

如何在基於ASP.NET MVC的網站中爲某些頁面使用HTTPS?ASP.NET MVC下的SSL頁面

史蒂夫·桑德森對如何在做到這一點在乾燥的方式上預覽4一個不錯的教程:

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

是否與預覽5?

+3

這是非常過時的。對於MVC4及更高版本,請參閱我的博文http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-new -allowanonymous-attribute.aspx – RickAndMSFT 2014-02-19 02:15:51

回答

1

這是使用ActionFilter的blog post by Adam Salvo

+0

確保你看到他自己寫的後續文章:http://blog.salvoz.com/2009/04/25/PartialSSLAndAuthorizationWithAspNetMVCRevisited.aspx – 2009-07-12 19:49:55

17

MVCFutures有'RequireSSL'屬性。

(感謝亞當爲pointing that out在更新的博文)

只要將它應用到你的操作方法,以「重定向=真正的」如果你想要一個http://請求自動變成https://開頭:

[RequireSsl(Redirect = true)] 

參見:ASP.NET MVC RequireHttps in Production Only

+0

我是否需要繼承它以處理本地主機請求? – 2009-11-05 02:46:41

+0

一種方法是爲本地機器創建證書並使用它。我認爲完全禁用它的本地主機,你確實需要子類或重複的代碼。不知道推薦的方法是什麼 – 2009-11-05 07:46:34

+1

看起來它是密封的,所以我需要重複代碼。遊民。 本地機器的證書只能在IIS中正常工作,而不是開發Web服務器。 – 2009-11-05 16:55:48

9

由於Amadiere wrote,[RequireHttps]在MVC 2 進入 HTTPS的偉大工程。但是,如果您只想按照您的說法使用HTTPS 一些頁面,那麼MVC 2不會給您任何愛 - 一旦它將用戶切換到HTTPS,它們就會一直停留在那裏,直到您手動重定向它們。

我使用的方法是使用另一個自定義屬性[ExitHttpsIfNotRequired]。當連接到控制器或動作,這將重定向到HTTP如果:

  1. 該請求是HTTPS
  2. 的[RequireHttps]屬性未施加到所述動作(或控制器)
  3. 請求是一個GET(重定向POST會導致各種麻煩)。

這裏有點太大而不能在這裏發佈,但你可以看到the code here加上一些額外的細節。

3

對於那些誰不是面向屬性的開發方法風扇,這裏是一段代碼,可以幫助:

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

有幾個原因,以避免屬性,其中之一是,如果你想要查看所有受保護頁面的列表,您必須跳過解決方案中的所有控制器。

2

我去翻過這個問題,希望我的解決方案可以幫助別人。

我們得到了幾個問題: - 我們需要保護的具體行動,例如「登錄」,在「帳戶」。我們可以使用RequireHttps屬性中的構建,這非常棒 - 但它會使用https://重定向到我們。 - 我們應該製作我們的鏈接,表單和諸如「SSL知曉」。

一般情況下,我的解決方案允許指定將使用絕對URL,除了指定協議的能力路線。您可以使用此批准來指定「https」協議。

所以,首先我創建了一個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 
} 

RequireSsl的現在,我已經創建手卷版本。我修改了原始的RequireSsl源代碼,允許重定向回http:// urls。另外,我已經把一個領域,使我們能夠確定我們是否應該要求SSL或沒有(我用它與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); 
    } 
} 

,這RequireSsl將做您的要求以下基本屬性值: - 忽略:不會做什麼。 - Http:將強制重定向到http協議。 - Https:將強制重定向到https協議。

你應該建立自己的基地控制器和設置該屬性爲HTTP。

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

現在,在每個需要SSL的cpntroller/action中,只需使用ConnectionProtocol.Https設置此屬性即可。

現在讓我們轉移到網址:我們得到了與URL路由引擎幾個問題。您可以通過http://blog.stevensanderson.com/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/瞭解更多關於它們的信息。在這篇文章中提出的解決方案理論上很好,但很老,我不喜歡這種方法。

我的解決方案如下: 創建基本的 「路」 類的子類:

公共類AbsoluteUrlRoute:路線 { #地區的構造函數

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

此版本的 「路線」 的類將創建絕對網址。這裏的訣竅,以及博客文章作者的建議,是使用DataToken來指定方案(例如結尾:))。

現在,如果我們要生成一個url,例如路徑「Account/LogOn」,我們會得到「/ http://example.com/Account/LogOn」 - 這是因爲UrlRoutingModule將所有的url視爲相對的。我們可以修復使用自定義的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); 
      } 
     } 
    } 
} 

由於該模塊是壓倒UrlRoutingModule的基本實現,我們應該卸下底座HTTP模塊,並在web.config中註冊我們的。所以,在「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> 

那就是:)。

爲了註冊一個絕對/協議遵循的路線,你應該做的:

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

會喜歡聽到你的反饋+的改進。希望它可以幫助! :)

編輯: 我忘了包括IsCurrentConnectionSecured()擴展方法(太多的片段:P)。這是一種通常使用Request.IsSecuredConnection的擴展方法。然而,當使用負載均衡時,這種方法無效 - 所以這種方法可以繞過這個(從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核心1.0)正在與Startup.cs略有不同。

要在所有頁面上使用RequireHttpsAttribute(如Amadiere的answer中所述),您可以將其添加到Startup.cs中,而不是在每個控制器上使用屬性樣式(或者不是爲所有控制器創建一個BaseController來繼承) 。

Startup.cs - 註冊過濾器:

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

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

有關的設計決定了上述方法的詳細信息,請參閱我在類似的問題有關how to exclude localhost requests from being handled by the RequireHttpsAttribute答案。