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);
}
}
Проектные решения объясняются:
- Используйте среду IsProduction () или IsDevelopment (), например, "#if DEBUG". Иногда мы выпускаем / публикуем в режиме отладки на нашем тестовом сервере и не хотим отключать это требование безопасности. Это нужно отключить только в localhost/development (поскольку нам лень настраивать localhost SSL в IIS Express или что-то, что мы используем локально).
- Используйте фильтр в Startup.cs для глобальной настройки (так как мы хотим, чтобы это применялось везде). Стартап должен отвечать за регистрацию и настройку всех глобальных правил. Если в вашей компании работает новый разработчик, она ожидает найти глобальные настройки в Startup.cs.
- Используйте логику 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
}