ASP.NET MVC RequireHttps in Production Only

前端 未结 15 1896
花落未央
花落未央 2020-11-28 18:23

I want to use the RequireHttpsAttribute to prevent unsecured HTTP requests from being sent to an action method.

C#

[RequireHttps] //apply to all acti         


        
15条回答
  •  伪装坚强ぢ
    2020-11-28 19:01

    After researching aroud, I was able to solve this issue with IIS Express and an override of the Controller class's OnAuthorization method (Ref#1). I have also gone with the route recommended by Hanselman (Ref#2). However, I was not complete satisfied with these two solutions due to two reasons: 1. Ref#1's OnAuthorization only works at the action level, not at the controller class level 2. Ref#2 requires a lot of setup (Win7 SDK for makecert), netsh commands, and, in order to use port 80 and port 443, I need to launch VS2010 as administrator, which I frown upon.

    So, I came up with this solution that focuses on simplicity with the following conditions:

    1. I want to be able to use the RequireHttps attbbute at Controller class or action level

    2. I want MVC to use HTTPS when the RequireHttps attribute is present, and use HTTP if it is absent

    3. I do not want to have to run Visual Studio as administrator

    4. I want to be able to use any HTTP and HTTPS ports that are assigned by IIS Express (See Note#1)

    5. I can reuse the self-signed SSL cert of IIS Express, and I do not care if I see the invalid SSL prompt

    6. I want dev, test, and production to have the exact same code base and same binary and as independent from additional setup (e.g. using netsh, mmc cert snap-in, etc.) as possible

    Now, with the background and explanation out of the way, I hope this code will help someone and save some time. Basically, create a BaseController class that inherits from Controller, and derive your controller classes from this base class. Since you have read this far, I assume that you know how to do these. So, happy coding!

    Note#1: This is achieved by the use of a useful function 'getConfig' (see code)

    Ref#1: http://puredotnetcoder.blogspot.com/2011/09/requirehttps-attribute-in-mvc3.html

    Ref#2: http://www.hanselman.com/blog/WorkingWithSSLAtDevelopmentTimeIsEasierWithIISExpress.aspx

    ========== Code in 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);
        }
    

    ============== end code ================

    In Web.Release.Config, add the following to clear out HttpPort and HttpsPort (to use the default 80 and 443).

    
    
    
    
    

提交回复
热议问题