ASP.NET MVC下的SSL页面

我如何去使用HTTPS在我的ASP.NET MVC的网站的一些页面?

史蒂夫桑德森有一个很好的教程,如何在干预的方式做预览4在:

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

预览版5有更好的/更新的方法吗?

如果您正在使用ASP.NET MVC 2 Preview 2或更高版本 ,现在可以简单地使用:

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

虽然, 这里提到的顺序参数值得注意。

MVCFutures具有'RequireSSL'属性。

(感谢Adam在你更新的blogpost中指出这一点 )

如果您希望http://请求自动变为https://://将其应用于您的操作方法,请使用'Redirect = true'

  [RequireSsl(Redirect = true)] 

另请参阅: 仅用于生产的ASP.NET MVC RequireHttps

正如Amadiere所写 ,[RequireHttps]在MVC 2中用于input HTTPS很有效。 但是,如果您只想为某些页面使用HTTPS,那么MVC 2不会给予您任何的爱 – 一旦将用户切换到HTTPS,它们就会一直停留在那里,直到您手动redirect它们。

我使用的方法是使用另一个自定义属性,[ExitHttpsIfNotRequired]。 当连接到控制器或操作时,如果出现以下情况,将redirect到HTTP:

  1. 请求是HTTPS
  2. [RequireHttps]属性未应用于操作(或控制器)
  3. 请求是一个GET(redirectPOST将导致各种麻烦)。

在这里发布有点太大了,但是你可以看到这里的代码以及一些额外的细节。

这是丹·华林最近发表的一篇文章:

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

他使用一个ActionFilter属性。

一些ActionLink扩展: http : //www.squaredroot.com/post/2008/06/11/MVC-and-SSL.aspx或redirect到https:// http://forums.asp.net的控制器操作属性/p/1260198/2358380.aspx#2358380

对于那些不喜欢面向属性的开发方法的人来说,这里有一段代码可以帮助:

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

有几个原因可以避免属性,其中之一是如果你想看看所有安全页面的列表,你将不得不跳过解决scheme中的所有控制器。

我讨论了这个问题,并希望我的解决scheme可以帮助别人。

我们得到的问题很less: – 我们需要确保具体的行动,例如“帐户”中的“login”。 我们可以使用RequireHttps属性中的构build,这非常棒 – 但它会使用https://redirect到我们。 – 我们应该build立我们的链接,表格和这样的“SSL意识”。

通常,我的解决scheme允许指定使用绝对URL的路由,此外还可以指定协议。 您可以使用此批准来指定“https”协议。

所以,首先我创build了一个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 } 

现在,我创build了RequireSsl的手卷版本。 我修改了原始的RequireSsl源代码,允许redirect回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将根据您的Requirements属性值来执行以下操作: – 忽略:不会执行任何操作。 – http:将强制redirect到http协议。 – https:将强制redirect到https协议。

您应该创build自己的基础控制器并将此属性设置为Http。

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

现在,在每个需要SSL的cpntroller / action中,只需使用ConnectionProtocol.Https设置该属性即可。

现在让我们移动到URL:我们没有什么问题的URL路由引擎。 你可以阅读更多关于他们在http://blog.stevensanderson.com/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/ 。 在这个post中提出的解决scheme理论上是好的,但老,我不喜欢接近。

我的解决scheme如下:创build一个基本的“路线”类的子类:

public class 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 } 

这个版本的“路线”类将创build绝对url。 这里的技巧,其次是博客文章作者的build议,是使用DataToken来指定scheme(例如最后:))。

现在,如果我们要生成一个url,例如path“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的基本实现,我们应该删除基本的httpModule并在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"; } 

这里是Pablo M. Cibrano在2009年1月发表的一篇博文 ,收集了一些技巧,包括HttpModule和扩展方法。

这里是亚当·萨尔沃(Adam Salvo)的博客文章,它使用了ActionFilter。

这不一定是MVC特定的,但是这个解决scheme对ASP.NET WebForms和MVC都有效:

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

我已经使用了好几年,喜欢通过web.config文件分离问题和pipe理。

MVC 6 (ASP.NET Core 1.0)与Startup.cs稍有不同。

要在所有页面上使用RequireHttpsAttribute(正如Amadiere的回答中所述),可以在Startup.cs中添加它,而不是在每个控制器上使用属性样式(或者不是为所有控制器创build一个BaseController来inheritance)。

Startup.cs – 注册filter:

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

有关上述方法的devise决策的更多信息,请参阅我有关如何排除由RequireHttpsAttribute处理的localhost请求的类似问题的回答 。