ASP.NET MVC: How to automatically disable [RequireHttps] on localhost?

后端 未结 5 990
深忆病人
深忆病人 2020-12-01 07:46

I want my login page to be SSL only:

    [RequireHttps]
    public ActionResult Login()
    {
        if (Helper.LoggedIn)
        {
            Response.Red         


        
相关标签:
5条回答
  • 2020-12-01 08:19

    The easiest thing would be to derive a new attribute from RequireHttps and override HandleNonHttpsRequest

    protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
            {
                if (!filterContext.HttpContext.Request.Url.Host.Contains("localhost"))
                {
                    base.HandleNonHttpsRequest(filterContext);
                }
            }
    

    HandleNonHttpsRequest is the method that throws the exception, here all we're doing is not calling it if the host is localhost (and as Jeff says in his comment you could extend this to test environments or in fact any other exceptions you want).

    0 讨论(0)
  • 2020-12-01 08:22
        public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
    
            if (!HttpContext.Current.IsDebuggingEnabled) {
                filters.Add(new RequireHttpsAttribute());
            }
        }
    
    0 讨论(0)
  • 2020-12-01 08:28

    You can encapsulate this requirement in a derived attribute:

    class RequireHttpsNonDebugAttribute : RequireHttpsAttribute {
        public override void HandleNonHttpsRequest(AuthorizationContext ctx) {
            #if (!DEBUG)
            base.HandleNonHttpsRequest(ctx);
            #endif
        }
    }
    
    0 讨论(0)
  • 2020-12-01 08:28

    MVC 6 (ASP.NET Core 1.0):

    The proper solution would be to use env.IsProduction() or env.IsDevelopment().

    Example:

    Startup.cs - AddMvc with a custom filter:

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

    Custom filter inherit from 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);
        }
    }
    

    Design decisions explained:

    1. Use environment IsProduction() or IsDevelopment() over e.g. "#if DEBUG". Sometimes we release/publish in DEBUG mode on our test server and don't want to disable this security requirement. This needs to be disabled only in localhost/development (since we are too lazy to setup localhost SSL in IIS Express or whatever we use locally).
    2. Use filter in Startup.cs for global setup (since we want this to apply everywhere). Startup should be responsible for registering and setting up all global rules. If your company employ a new developer, she would expect to find global setup in Startup.cs.
    3. Use RequireHttpsAttribute logic since it's proven (by Microsoft). The only thing we want to change is when logic is applied (production) and when it's not (development/localhost). Never use "magical" strings like "http://" and "https://" when it can be avoided by reusing a Microsoft component created to provide the same logic.

    Above I would consider the "proper" solution.

    Note:

    As an alternative, we could make a "class BaseController : Controller" and make all our controllers inherit from "BaseController" (instead of Controller). Then we only have to set the attribute 1 global place (and don't need to register filter in Startup.cs).

    Some people prefer the attribute style. Please note this will eliminate design decision #2's benefits.

    Example of usage:

    [RequireHttpsInProductionAttribute]
    public class BaseController : Controller
    {
        // Maybe you have other shared controller logic..
    }
    
    public class HomeController : BaseController
    {
        // Add endpoints (GET / POST) for Home controller
    }
    
    0 讨论(0)
  • 2020-12-01 08:34
    #if (!DEBUG)
    [RequireHttps]
    #endif
    public ActionResult Login()
    {
        if (Helper.LoggedIn)
        {
            Response.Redirect("/account/stats");
        }
    
        return View();
    }
    
    0 讨论(0)
提交回复
热议问题