ASP.NET MVC: как автоматически отключить [RequireHttps] на локальном хосте? - PullRequest
27 голосов
/ 02 сентября 2010

Я хочу, чтобы моя страница входа была только SSL:

    [RequireHttps]
    public ActionResult Login()
    {
        if (Helper.LoggedIn)
        {
            Response.Redirect("/account/stats");
        }

        return View();
    }

Но, очевидно, он не работает на localhost, когда я разрабатываю и отлаживаю свое приложение. Я не хочу использовать IIS 7 с сертификатами SSL, как я могу автоматически отключить атрибут RequireHttps?

Обновление

На основе информации, предоставленной пользователями StackOverflow, и исходного кода ASP.NET MVC 2, я создал следующий класс, который решает проблему.

public class RequireSSLAttribute : FilterAttribute, IAuthorizationFilter
{
    public virtual void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        if (!filterContext.HttpContext.Request.IsSecureConnection)
        {
            HandleNonHttpsRequest(filterContext);
        }
    }

    protected virtual void HandleNonHttpsRequest(AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.Url.Host.Contains("localhost")) return;

        if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("The requested resource can only be accessed via SSL");
        }

        string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
        filterContext.Result = new RedirectResult(url);
    }
}

И он используется так:

[RequireSSL]
public ActionResult Login()
{
    if (Helper.LoggedIn)
    {
        Response.Redirect("/account/stats");
    }

    return View();
}

Ответы [ 5 ]

26 голосов
/ 02 сентября 2010

Проще всего было бы получить новый атрибут из RequireHttps и переопределить HandleNonHttpsRequest

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

HandleNonHttpsRequest - это метод, который выдает исключение, здесь все, что мы делаем, это не вызываем его, если хостlocalhost (и, как говорит Джефф в своем комментарии, вы можете расширить это на тестовые среды или на любые другие исключения, которые вы хотите).

19 голосов
/ 05 декабря 2014
    public static void RegisterGlobalFilters(GlobalFilterCollection filters) {

        if (!HttpContext.Current.IsDebuggingEnabled) {
            filters.Add(new RequireHttpsAttribute());
        }
    }
14 голосов
/ 02 сентября 2010
#if (!DEBUG)
[RequireHttps]
#endif
public ActionResult Login()
{
    if (Helper.LoggedIn)
    {
        Response.Redirect("/account/stats");
    }

    return View();
}
8 голосов
/ 02 сентября 2010

Вы можете заключить это требование в производный атрибут:

class RequireHttpsNonDebugAttribute : RequireHttpsAttribute {
    public override void HandleNonHttpsRequest(AuthorizationContext ctx) {
        #if (!DEBUG)
        base.HandleNonHttpsRequest(ctx);
        #endif
    }
}
1 голос
/ 07 июля 2016

MVC 6 (ASP.NET Core 1.0):

Правильным решением будет использование env.IsProduction () или env.IsDevelopment ().

Пример:

Startup.cs - AddMvc с пользовательским фильтром:

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

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

Пользовательский фильтр наследуется от 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);
    }
}

Объяснение проектных решений:

  1. Использовать среду IsProduction () или IsDevelopment (), например, "#if DEBUG".Иногда мы выпускаем / публикуем в режиме отладки на нашем тестовом сервере и не хотим отключать это требование безопасности.Это нужно отключить только в localhost / development (так как нам лень настраивать localhost SSL в IIS Express или что-то, что мы используем локально).
  2. Использовать фильтр в Startup.cs для глобальногоустановка (так как мы хотим, чтобы это применялось везде).Стартап должен отвечать за регистрацию и настройку всех глобальных правил.Если в вашей компании будет работать новый разработчик, она ожидает найти глобальные настройки в файле Startup.cs.
  3. Использовать RequireHttpsAttribute логику, поскольку она доказана (Microsoft).Единственное, что мы хотим изменить - это когда применяется логика (производство), а когда нет (разработка / локальный хост).Никогда не используйте «волшебные» строки, такие как «http://"» и «https://", если этого можно избежать, повторно используя компонент Microsoft, созданный для обеспечения той же логики.

Выше я бы назвал« правильным »решение.

Примечание:

В качестве альтернативы , мы могли бы создать «класс BaseController: Controller» и заставить все наши контроллеры наследовать от «BaseController "(вместо контроллера).Тогда нам нужно только установить глобальное место атрибута 1 (и не нужно регистрировать фильтр в Startup.cs).

Некоторые люди предпочитают стиль атрибута.Обратите внимание, что это исключит преимущества проектного решения № 2.

Пример использования:

[RequireHttpsInProductionAttribute]
public class BaseController : Controller
{
    // Maybe you have other shared controller logic..
}

public class HomeController : BaseController
{
    // Add endpoints (GET / POST) for Home controller
}
...