ASP.NET MVC下的SSL页面


80

如何在基于ASP.NET MVC的站点中的某些页面上使用HTTPS?

史蒂夫·桑德森(Steve Sanderson)在Preview 4上以DRY方式提供了一个很好的教程,该教程以DRY方式进行:

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

预览5是否有更好/更新的方式?,


3
这是过时的。对于MVC4高,看我的博客文章blogs.msdn.com/b/rickandy/archive/2012/03/23/...
RickAndMSFT

Answers:


92

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

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

虽然,命令的参数是值得注意的,因为这里所说的


23
您也可以在控制器级别执行此操作。更好的是,如果您希望整个应用程序都是SSL,则可以创建基本控制器,将其扩展到所有控制器,然后在其中应用属性。
ashes999

22
或者,您可以在Global.asax GlobalFilters.Filters.Add(new RequireHttpsAttribute());中添加一个全局过滤器MVC3。
GraemeMiller

2
无法保证其他开发人员将使用您派生的控制器。您只需打一个电话给力HTTPS -见我的博客文章blogs.msdn.com/b/rickandy/archive/2012/03/23/...
RickAndMSFT

17

MVCFutures具有“ RequireSSL”属性。

(感谢亚当在更新的博客文章中指出这一点

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

    [RequireSsl(Redirect = true)]

另请参阅:仅在生产中的ASP.NET MVC RequireHttps


为了处理本地主机请求,是否必须将其子类化?
罗杰斯先生

一种方法是为本地计算机创建证书并使用它。我认为要为localhost完全禁用它,您确实需要继承或复制代码。不确定推荐的方法是什么
Simon_Weaver

1
看起来像是密封的,所以我需要重复代码。笨蛋 尽管正确,但本地计算机的证书只能在IIS中工作,而不能在dev Web服务器中使用。
罗杰斯先生2009年


更新这MVC4 +看我的博客文章blogs.msdn.com/b/rickandy/archive/2012/03/23/...
RickAndMSFT

9

正如Amadiere所写,[RequireHttps]在MVC 2中非常适合输入HTTPS。但是,如果您只想像您所说的那样对某些页面使用HTTPS ,则MVC 2不会给您任何好处-一旦将用户切换到HTTPS,他们就会被卡在那里,直到您手动重定向它们为止。

我使用的方法是使用另一个自定义属性[ExitHttpsIfNotRequired]。附加到控制器或操作后,如果出现以下情况,它将重定向到HTTP:

  1. 该请求是HTTPS
  2. [RequireHttps]属性未应用于操作(或控制器)
  3. 该请求是GET(重定向POST会导致各种麻烦)。

它太大了,无法在此处发布,但是您可以在此处看到代码以及一些其他详细信息。


AllowAnonymous可解决此问题。对于MVC4高,看我的博客文章blogs.msdn.com/b/rickandy/archive/2012/03/23/...
RickAndMSFT

8

这是Dan Wahlin最近发表的一篇文章:

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

他使用一个ActionFilter属性。


2
目前看来,这是最好的方法。
2009年

一年后+1,因为isLocal通话帮助我解决了一个问题,此问题在@@@上变得非常痛苦
heisenberg 2010年

1
上面的日期是,对于MVC4高,看我的博客文章blogs.msdn.com/b/rickandy/archive/2012/03/23/...
RickAndMSFT


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

避免使用属性有多种原因,其中之一是,如果要查看所有受保护页面的列表,则必须跳过解决方案中的所有控制器。


我认为大多数人都不会同意您的意见,尽管提供另一种方法总是有用的……
Serj Sagan 2014年

2

我遇到了这个问题,希望我的解决方案可以对某人有所帮助。

我们遇到的问题很少:-我们需要保护特定的操作,例如“帐户”中的“登录”。我们可以使用RequireHttps属性中的build,这很棒,但是它将使用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:// url。另外,我放置了一个字段,使我们可以确定是否需要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:将强制重定向到http协议。-Https:将强制重定向到https协议。

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

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

现在,在每个想要使用SSL的cpntroller /操作中,只需使用ConnectionProtocol.Https设置此属性。

现在转到URL:url路由引擎几乎没有问题。您可以在http://blog.stevensanderson.com/2008/08/05/adding-httpsssl-support-to-aspnet-mvc-routing/上了解有关它们的更多信息。从理论上讲,本文中建议的解决方案很好,但是过时了,我不喜欢这种方式。

我的解决方案如下:创建基本“ Route”类的子类:

公共类AbsoluteUrlRoute:路线{#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
}

此版本的“ Route”类将创建绝对URL。此处的技巧以及博客文章作者的建议是使用DataToken指定方案(末尾的示例:))。

现在,如果我们生成一个URL,例如路由“ Account / LogOn”,我们将得到“ / http://example.com/Account/LogOn”-这是因为UrlRoutingModule将所有URL视为相对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";
    }




0

MVC 6(ASP.NET Core 1.0)与Startup.cs的工作略有不同。

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

Startup.cs-注册过滤器:

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

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

有关上述方法的设计决策的更多信息,请参见我对类似问题的回答,该问题有关如何从RequireHttpsAttribute中排除本地主机请求


0

或者将过滤器添加到Global.asax.cs

GlobalFilters.Filters.Add(new RequireHttpsAttribute());

RequireHttpsAttribute类

using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace xxxxxxxx
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            GlobalFilters.Filters.Add(new RequireHttpsAttribute());
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.