ASP.NET MVC: как автоматически отключить [RequireHttps] на локальном хосте?

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

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

        return View();
    }

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

Обновить

На основе информации, предоставленной пользователями Stackru и исходным кодом 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 ответов

Решение

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

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

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

    public static void RegisterGlobalFilters(GlobalFilterCollection filters) {

        if (!HttpContext.Current.IsDebuggingEnabled) {
            filters.Add(new RequireHttpsAttribute());
        }
    }
#if (!DEBUG)
[RequireHttps]
#endif
public ActionResult Login()
{
    if (Helper.LoggedIn)
    {
        Response.Redirect("/account/stats");
    }

    return View();
}

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

class RequireHttpsNonDebugAttribute : RequireHttpsAttribute {
    public override void HandleNonHttpsRequest(AuthorizationContext ctx) {
        #if (!DEBUG)
        base.HandleNonHttpsRequest(ctx);
        #endif
    }
}

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" (вместо Controller). Тогда нам нужно только установить глобальное место атрибута 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
}
Другие вопросы по тегам