2008-10-01 112 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答案。