代码之家  ›  专栏  ›  技术社区  ›  Zack Peterson

ASP.NET MVC仅在生产中需要TTP

  •  119
  • Zack Peterson  · 技术社区  · 15 年前

    我想使用 RequireHttpsAttribute 防止将不安全的HTTP请求发送到操作方法。

    C#

    [RequireHttps] //apply to all actions in controller
    public class SomeController 
    {
        [RequireHttps] //apply to this action only
        public ActionResult SomeAction()
        {
            ...
        }
    }
    

    VB

    <RequireHttps()> _
    Public Class SomeController
    
        <RequireHttps()> _
        Public Function SomeAction() As ActionResult
            ...
        End Function
    
    End Class
    

    不幸的是,ASP.NET开发服务器不支持HTTPS。

    如何使我的ASP.NET MVC应用程序在发布到生产环境时使用RequireHttps,而不是在ASP.NET开发服务器上的开发工作站上运行时使用RequireHttps?

    15 回复  |  直到 15 年前
        1
  •  129
  •   Joel Mueller    15 年前

    如果在开发工作站上运行版本构建,这将没有帮助,但是条件编译可以完成这项工作。。。

    #if !DEBUG
    [RequireHttps] //apply to all actions in controller
    #endif
    public class SomeController 
    {
        //... or ...
    #if !DEBUG
        [RequireHttps] //apply to this action only
    #endif
        public ActionResult SomeAction()
        {
        }
    
    }
    

    #If Not Debug Then
        <RequireHttps()> _
        Function SomeAction() As ActionResult
    #Else
        Function SomeAction() As ActionResult
    #End If
            ...
        End Function
    

    更新2

    有几个人提到了源于 RequireHttpsAttribute

    免责声明:我还没有测试过这段代码,哪怕是一点点,而且我的VB已经相当生锈了。我只知道它是编译的。我是根据spot、queen3和Lance Fisher的建议写的。如果它不起作用,它至少应该传达总体思路,并给你一个起点。

    Public Class RemoteRequireHttpsAttribute
        Inherits System.Web.Mvc.RequireHttpsAttribute
    
        Public Overrides Sub OnAuthorization(ByVal filterContext As  _
                                             System.Web.Mvc.AuthorizationContext)
            If IsNothing(filterContext) Then
                Throw New ArgumentNullException("filterContext")
            End If
    
            If Not IsNothing(filterContext.HttpContext) AndAlso _
                filterContext.HttpContext.Request.IsLocal Then
                Return
            End If
    
            MyBase.OnAuthorization(filterContext)
        End Sub
    
    End Class
    

    基本上,如果当前请求是本地的(即通过localhost访问站点),则新属性将退出,而不是运行默认的SSL授权代码。您可以这样使用它:

    <RemoteRequireHttps()> _
    Public Class SomeController
    
        <RemoteRequireHttps()> _
        Public Function SomeAction() As ActionResult
            ...
        End Function
    
    End Class
    

    干净多了!如果我的未经测试的代码确实有效。

        2
  •  65
  •   Dan Atkinson    13 年前

    如果有人需要C版本:

    using System;
    using System.Web.Mvc;
    
    namespace My.Utils
    {
        public class MyRequireHttpsAttribute : RequireHttpsAttribute
        {
            public override void OnAuthorization(AuthorizationContext filterContext)
            {
                if (filterContext == null)
                {
                    throw new ArgumentNullException("filterContext");
                }
    
                if (filterContext.HttpContext != null && filterContext.HttpContext.Request.IsLocal)
                {
                    return;
                }
    
                base.OnAuthorization(filterContext);
            }
        }
    }
    
        3
  •  26
  •   stephenbayer    10 年前

    若要完全避开此问题,您也可以在本地计算机上使用IIS并使用自签名证书。IIS比内置的Web服务器更快,而且您的开发环境更像是生产环境。

    Scott Hanselman has a great resource on a few ways to implement local HTTPS with VS2010 and IIS Express.

        4
  •  12
  •   gt124    13 年前

    利用MVC过滤系统和Global.asax.cs,我假设您可以做到这一点。。。

        protected void Application_Start()
        {
          RegisterGlobalFilters(GlobalFilters.Filters);
        }
    
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
          filters.Add(new HandleErrorAttribute());
          if(Config.IsProduction) //Some flag that you can tell if you are in your production environment.
          {
            filters.Add(new RequireHttpsAttribute());
          }
        }
    
        5
  •  10
  •   Samuel Jack    12 年前

    由于是ASP.Net开发服务器首先导致了您的问题,因此值得注意的是,Microsoft现在已经解决了这个问题 IIS Express

    Scott Hanselman在 working with SSL in IIS Express .

        6
  •  9
  •   spot    15 年前

        7
  •  4
  •   Eric Beijner    8 年前

    这对我很有效, 代码检查调试是否正在开发中,如果没有,则不需要ssl。 所有编辑都在中 .

    加:

    private IHostingEnvironment CurrentEnvironment { get; set; }
    

    加:

    public Startup(IHostingEnvironment env)
    {
        CurrentEnvironment = env;
    }
    

    编辑:

    public void ConfigureServices(IServiceCollection services)
    {
        // additional services...
    
        services.AddMvc(options =>
        {
            if (!CurrentEnvironment.IsDevelopment())
            {
                options.Filters.Add(typeof(RequireHttpsAttribute));
            }
        });
    }
    
        8
  •  3
  •   queen3    15 年前

    如果您可以派生和重写,请执行该操作。如果您不能-MVC附带源代码,只需获取源代码并创建自己的[ForceHttps]属性来检查IsLocal。

        9
  •  3
  •   juhan_h    13 年前

    对于MVC 3,我添加了自己的FilterProvider(基于以下代码: Global and Conditional Filters RequireHttpsAttribute 什么时候 HttpContext.Request.IsLocal == false .

        10
  •  3
  •   Leng Keng    12 年前

    经过研究,我能够通过IIS Express和重写控制器类的OnAuthorization方法(参考1)解决这个问题。我也选择了Hanselman推荐的路线(参考文献2)。然而,由于两个原因,我对这两种解决方案并不完全满意: 1.Ref#1的授权仅在操作级别有效,而不在控制器类级别有效

    1. 我希望MVC在RequireHttps属性存在时使用HTTPS,如果不存在,则使用HTTP

    2. 我可以重用IIS Express的自签名SSL证书,我不在乎是否看到无效的SSL提示

    3. 我希望开发、测试和生产具有完全相同的代码库和相同的二进制文件,并且尽可能独立于其他设置(例如,使用netsh、mmc证书管理单元等)

    注#1:这是通过使用有用的函数“getConfig”实现的(参见代码)

    参考文献1: http://puredotnetcoder.blogspot.com/2011/09/requirehttps-attribute-in-mvc3.html

    http://www.hanselman.com/blog/WorkingWithSSLAtDevelopmentTimeIsEasierWithIISExpress.aspx

    ==========BaseController中的代码===================

         #region Override to reroute to non-SSL port if controller action does not have RequireHttps attribute to save on CPU 
        // By L. Keng, 2012/08/27
        // Note that this code works with RequireHttps at the controller class or action level.
        // Credit: Various stackoverflow.com posts and http://puredotnetcoder.blogspot.com/2011/09/requirehttps-attribute-in-mvc3.html
        protected override void OnAuthorization(AuthorizationContext filterContext)
        {
            // if the controller class or the action has RequireHttps attribute
            var requireHttps = (filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(RequireHttpsAttribute), true).Count() > 0 
                                || filterContext.ActionDescriptor.GetCustomAttributes(typeof(RequireHttpsAttribute), true).Count() > 0);
            if (Request.IsSecureConnection)
            {
                // If request has a secure connection but we don't need SSL, and we are not on a child action   
                if (!requireHttps && !filterContext.IsChildAction)
                {
                    var uriBuilder = new UriBuilder(Request.Url)
                    {
                        Scheme = "http",
                        Port = int.Parse(getConfig("HttpPort", "80")) // grab from config; default to port 80
                    };
                    filterContext.Result = this.Redirect(uriBuilder.Uri.AbsoluteUri);
                }
            }
            else
            {
                // If request does not have a secure connection but we need SSL, and we are not on a child action   
                if (requireHttps && !filterContext.IsChildAction)
                {
                    var uriBuilder = new UriBuilder(Request.Url)
                    {
                        Scheme = "https",
                        Port = int.Parse(getConfig("HttpsPort", "443")) // grab from config; default to port 443
                    };
                    filterContext.Result = this.Redirect(uriBuilder.Uri.AbsoluteUri);
                }
            }
            base.OnAuthorization(filterContext);
        }
        #endregion
    
        // a useful helper function to get appSettings value; allow caller to specify a default value if one cannot be found
        internal static string getConfig(string name, string defaultValue = null)
        {
            var val = System.Configuration.ConfigurationManager.AppSettings[name];
            return (val == null ? defaultValue : val);
        }
    

    在Web.Release.Config中,添加以下内容以清除HttpPort和HttpsPort(使用默认的80和443)。

    <appSettings>
    <add key="HttpPort" value="" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
    <add key="HttpsPort" value="" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
    </appSettings>
    
        11
  •  3
  •   Nick    11 年前

    您可以在生产工作站和开发工作站上使用一种解决方案。它基于web.config中应用程序设置中的选项

    <appSettings>
         <!--Use SSL port 44300 in IIS Express on development workstation-->
         <add key="UseSSL" value="44300" />
    </appSettings>
    

    然后使用 要求属性 那会照顾好你的情况。它实际上是从 除添加条件外,使用与基本方法相同的实现。

    public class RequireHttpsConditional : RequireHttpsAttribute
    {
        protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
        {
            var useSslConfig = ConfigurationManager.AppSettings["UseSSL"];
            if (useSslConfig != null)
            {
                if (!string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
                {
                    throw new InvalidOperationException("The requested resource can only be accessed via SSL.");
                }
    
                var request = filterContext.HttpContext.Request;
                string url = null;
                int sslPort;
    
                if (Int32.TryParse(useSslConfig, out sslPort) && sslPort > 0)
                {
                    url = "https://" + request.Url.Host + request.RawUrl;
    
                    if (sslPort != 443)
                    {
                        var builder = new UriBuilder(url) {Port = sslPort};
                        url = builder.Uri.ToString();
                    }
                }
    
                if (sslPort != request.Url.Port)
                {
                    filterContext.Result = new RedirectResult(url);
                }
            }
        }
    }
    

    别忘了装饰 登录 AccountController中的方法

    [RequireHttpsConditional]
    [HttpPost]
    public ActionResult LogOn(LogOnModel model, string returnUrl)
    

    在你的房间里有这样的东西 登录 查看以便通过https发布表单。

    <% using (Html.BeginFormSecure("LogOn", "Account", new { ReturnUrl = Request.QueryString["ReturnUrl"] }, Request.IsSecureConnection, Request.Url)) { %>
    
        12
  •  2
  •   Jose    14 年前

    正如Joel提到的,您可以使用 #if !DEBUG

        13
  •  1
  •   Community CDub    7 年前

    MVC6

    正确的解决方案是使用env.IsProduction()或env.IsDevelopment()。阅读更多关于此答案背后原因的信息 how to require https only in production .

    以下两种不同风格的简明答案(请参阅上面的链接以了解更多有关设计决策的信息):

    1. Startup.cs-寄存器过滤器

    Startup.cs

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

    (属性样式):

    [RequireHttpsInProductionAttribute]
    public class BaseController : Controller
    {
        // Maybe you have other shared controller logic..
    }
    
    public class HomeController : BaseController
    {
        // Add endpoints (GET / POST) for Home controller
    }
    

    RequireAttribute产品属性 : RequireHttpsAttribute :

    public class RequireHttpsInProductionAttribute : RequireHttpsAttribute
    {
        private bool IsProduction { get; }
    
        public RequireHttpsInProductionAttribute(IHostingEnvironment environment)
        {
            if (environment == null)
                throw new ArgumentNullException(nameof(environment));
            this.IsProduction = environment.IsProduction(); 
        }
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (this.IsProduction)
                base.OnAuthorization(filterContext);
        }
        protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
        {
            if(this.IsProduction)
                base.HandleNonHttpsRequest(filterContext);
        }
    }
    
        14
  •  1
  •   Carter Medlin    8 年前

    这对我来说是最干净的方式。在我的 App_Start\FilterConfig.cs

    ... 
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
            if (!Web.HttpContext.Current.IsDebuggingEnabled) {
                filters.Add(new RequireHttpsAttribute());   
            }
            ...
    }
    

    或者,您可以将其设置为仅在自定义错误页打开时需要https。

    ... 
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
            if (Web.HttpContext.Current.IsCustomErrorEnabled) {
                filters.Add(new RequireHttpsAttribute());   
            }
            ...
    }