ASP.NET MVC RequireHttps только в производстве
Я хочу использовать RequireHttpsAttribute, чтобы предотвратить отправку необработанных HTTP-запросов методу действия.
С#
[RequireHttps] //apply to all actions in controller
public class SomeController
{
[RequireHttps] //apply to this action only
public ActionResult SomeAction()
{
...
}
}
В. Б.
<RequireHttps()> _
Public Class SomeController
<RequireHttps()> _
Public Function SomeAction() As ActionResult
...
End Function
End Class
К сожалению, ASP.NET Development Server не поддерживает HTTPS.
Как я могу заставить приложение ASP.NET MVC использовать RequireHttps при публикации в рабочей среде, но не при запуске на моей рабочей станции разработки на сервере разработки ASP.NET?
Ответы
Ответ 1
Это не поможет, если вы запускаете сборки Release на рабочей станции разработки, но условная компиляция может выполнить эту работу...
#if !DEBUG
[RequireHttps] //apply to all actions in controller
#endif
public class SomeController
{
//... or ...
#if !DEBUG
[RequireHttps] //apply to this action only
#endif
public ActionResult SomeAction()
{
}
}
Update
В Visual Basic атрибуты являются технически частью той же линии, что и определение, к которому они относятся. Вы не можете вводить условные инструкции компиляции внутри строки, поэтому вам приходится дважды писать объявление функции - один раз с атрибутом и один раз без него. Это действительно работает, если вы не против уродства.
#If Not Debug Then
<RequireHttps()> _
Function SomeAction() As ActionResult
#Else
Function SomeAction() As ActionResult
#End If
...
End Function
Обновление 2
Несколько человек упомянули вывод из RequireHttpsAttribute
без предоставления примера, так что здесь один для вас. Я думаю, что этот подход был бы намного более чистым, чем подход условной компиляции, и это было бы моим преимуществом в вашей позиции.
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я не тестировал этот код, даже немного, и мой VB довольно ржавый. Все, что я знаю, это то, что он компилируется. Я написал его на основе предложений spot, queen3 и Lance Fisher. Если он не работает, он должен хотя бы передать общую идею и дать вам отправную точку.
Public Class RemoteRequireHttpsAttribute
Inherits System.Web.Mvc.RequireHttpsAttribute
Public Overrides Sub OnAuthorization(ByVal filterContext As _
System.Web.Mvc.AuthorizationContext)
If IsNothing(filterContext) Then
Throw New ArgumentNullException("filterContext")
End If
If Not IsNothing(filterContext.HttpContext) AndAlso _
filterContext.HttpContext.Request.IsLocal Then
Return
End If
MyBase.OnAuthorization(filterContext)
End Sub
End Class
В принципе, новый атрибут просто завершает работу вместо запуска кода авторизации SSL по умолчанию, если текущий запрос является локальным (то есть вы получаете доступ к сайту через localhost). Вы можете использовать его следующим образом:
<RemoteRequireHttps()> _
Public Class SomeController
<RemoteRequireHttps()> _
Public Function SomeAction() As ActionResult
...
End Function
End Class
Гораздо чище! Если мой незапрошенный код действительно работает.
Ответ 2
Если кому-то нужна версия С#:
using System;
using System.Web.Mvc;
namespace My.Utils
{
public class MyRequireHttpsAttribute : RequireHttpsAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.HttpContext != null && filterContext.HttpContext.Request.IsLocal)
{
return;
}
base.OnAuthorization(filterContext);
}
}
}
Ответ 3
Вывод из RequireHttps - хороший подход.
Чтобы полностью решить проблему, вы можете использовать IIS на своей локальной машине с самозаверяющим сертификатом. IIS быстрее, чем встроенный веб-сервер, и у вас есть то преимущество, что ваша среда разработки больше похожа на процесс производства.
Скотт Ханзельман обладает отличным ресурсом по нескольким способам реализации локальных HTTPS с VS2010 и IIS Express.
Ответ 4
Как и прежде, ASP.NET Development Server вызвал вашу проблему, стоит отметить, что Microsoft теперь IIS Express, который поставляется с Visual Studio (начиная с VS2010 SP1). Это сокращенная версия IIS, которая так же проста в использовании, как и сервер разработки, но поддерживает полный набор функций IIS 7.5, включая SSL.
Скотт Ханзельман имеет подробное сообщение о работе с SSL в IIS Express.
Ответ 5
Используя систему фильтров MVC и Global.asax.cs, я предполагаю, что вы можете это сделать...
protected void Application_Start()
{
RegisterGlobalFilters(GlobalFilters.Filters);
}
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
if(Config.IsProduction) //Some flag that you can tell if you are in your production environment.
{
filters.Add(new RequireHttpsAttribute());
}
}
Ответ 6
Как насчет наследования атрибута RequireHttps в пользовательском атрибуте. Затем, внутри вашего настраиваемого атрибута, проверьте свойство IsLocal текущего запроса, чтобы узнать, поступает ли запрос с локальной машины. Если это так, не применяйте базовую функциональность. В противном случае вызовите базовую операцию.
Ответ 7
Если вы можете получить и переопределить - сделайте это. Если вы не можете - MVC поставляется с источниками, просто возьмите источники и создайте свой собственный атрибут [ForceHttps], который проверяет IsLocal.
Ответ 8
Для MVC 3 я добавил свой собственный FilterProvider (на основе кода, найденного здесь: Глобальные и условные фильтры, которые, среди прочего (отображение информации Debug для локальных пользователей и т.д.), украсит все действия с помощью RequireHttpsAttribute
, когда HttpContext.Request.IsLocal == false
.
Ответ 9
После исследования aroud я смог решить эту проблему с помощью IIS Express и переопределить метод OnAuthorization класса Controller (Ref # 1). Я также отправился с маршрутом, рекомендованным Hanselman (Ref № 2). Однако я не был полностью удовлетворен этими двумя решениями по двум причинам:
1. Ссылка № 1 OnAuthorization работает только на уровне действия, а не на уровне класса контроллера
2. Ссылка № 2 требует большой настройки (Win7 SDK для makecert), команд netsh, и, чтобы использовать порт 80 и порт 443, мне нужно запустить VS2010 в качестве администратора, на который я нахмурился.
Итак, я придумал это решение, которое фокусируется на простоте со следующими условиями:
-
Я хочу, чтобы использовать RequireHttps attbbute в классе Controller или уровне действия
-
Я хочу, чтобы MVC использовал HTTPS, когда присутствует атрибут RequireHttps, и используйте HTTP, если он отсутствует
-
Я не хочу запускать Visual Studio в качестве администратора
-
Я хочу иметь возможность использовать любые HTTP и HTTPS-порты, назначенные IIS Express (см. примечание № 1)
-
Я могу повторно использовать самоподписанный SSL-сертификат IIS Express, и мне все равно, вижу ли я неверное приглашение SSL
-
Я хочу, чтобы dev, test и production имели ту же самую базу кода и одну и ту же двоичную и независимую от дополнительной настройки (например, используя netsh, mmc cert snap-in и т.д.), насколько это возможно
Теперь, с учетом фона и объяснений, я надеюсь, что этот код поможет кому-то и сэкономит время. В принципе, создайте класс BaseController, который наследуется от Controller, и выведите классы контроллера из этого базового класса. Поскольку вы зачитали это далеко, я предполагаю, что вы знаете, как это сделать. Итак, счастливая кодировка!
Примечание № 1: Это достигается за счет использования полезной функции getConfig (см. код)
Ссылка № 1: http://puredotnetcoder.blogspot.com/2011/09/requirehttps-attribute-in-mvc3.html
Ссылка № 2: http://www.hanselman.com/blog/WorkingWithSSLAtDevelopmentTimeIsEasierWithIISExpress.aspx
========== Код в 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);
}
============== конечный код ================
В Web.Release.Config добавьте следующее, чтобы очистить HttpPort и HttpsPort (использовать значения по умолчанию 80 и 443).
<appSettings>
<add key="HttpPort" value="" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
<add key="HttpsPort" value="" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
</appSettings>
Ответ 10
Одно решение, которое вы можете использовать как на производстве, так и на рабочей станции разработки. Он основан на вашей опции из настроек приложения в web.config
<appSettings>
<!--Use SSL port 44300 in IIS Express on development workstation-->
<add key="UseSSL" value="44300" />
</appSettings>
Если вы не хотите использовать SSL, удалите ключ. Если вы используете стандартный порт SSL 443, удалите это значение или укажите 443.
Затем используйте пользовательскую реализацию RequireHttpsAttribute, которая позаботится о вашем состоянии. Он фактически получен из RequireHttps и использует ту же реализацию базового метода, за исключением добавления условий.
public class RequireHttpsConditional : RequireHttpsAttribute
{
protected override void HandleNonHttpsRequest(AuthorizationContext filterContext)
{
var useSslConfig = ConfigurationManager.AppSettings["UseSSL"];
if (useSslConfig != null)
{
if (!string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("The requested resource can only be accessed via SSL.");
}
var request = filterContext.HttpContext.Request;
string url = null;
int sslPort;
if (Int32.TryParse(useSslConfig, out sslPort) && sslPort > 0)
{
url = "https://" + request.Url.Host + request.RawUrl;
if (sslPort != 443)
{
var builder = new UriBuilder(url) {Port = sslPort};
url = builder.Uri.ToString();
}
}
if (sslPort != request.Url.Port)
{
filterContext.Result = new RedirectResult(url);
}
}
}
}
Не забудьте украсить метод LogOn в AccountController
[RequireHttpsConditional]
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
и что-то вроде этого в вашем представлении LogOn для отправки формы через https.
<% using (Html.BeginFormSecure("LogOn", "Account", new { ReturnUrl = Request.QueryString["ReturnUrl"] }, Request.IsSecureConnection, Request.Url)) { %>
Ответ 11
Это сработало для меня, MVC 6 (ASP.NET Core 1.0).
Код проверяет, находится ли debug в разработке, а если нет, ssl не требуется.
Все изменения находятся в Startup.cs.
Добавить
private IHostingEnvironment CurrentEnvironment { get; set; }
Добавить
public Startup(IHostingEnvironment env)
{
CurrentEnvironment = env;
}
Edit:
public void ConfigureServices(IServiceCollection services)
{
// additional services...
services.AddMvc(options =>
{
if (!CurrentEnvironment.IsDevelopment())
{
options.Filters.Add(typeof(RequireHttpsAttribute));
}
});
}
Ответ 12
Как сказал Джоэл, вы можете изменить компиляцию с помощью директивы #if !DEBUG
.
Я только узнал, что вы можете изменить значение символа DEBUG в элементе компиляции файла web.config. Надеюсь, что это поможет.
Ответ 13
MVC 6 (ASP.NET Core 1.0):
Правильным решением было бы использовать env.IsProduction() или env.IsDevelopment(). Узнайте больше о причине в этом ответе о том, как требовать https только в процессе производства.
Сжатый ответ ниже (см. ссылку выше, чтобы узнать больше о дизайнерских решениях) для двух разных стилей:
- Startup.cs - зарегистрировать фильтр
- BaseController - стиль атрибута
Startup.cs (фильтр регистрации):
public void ConfigureServices(IServiceCollection services)
{
// TODO: Register other services
services.AddMvc(options =>
{
options.Filters.Add(typeof(RequireHttpsInProductionAttribute));
});
}
BaseController.cs (стиль атрибута):
[RequireHttpsInProductionAttribute]
public class BaseController : Controller
{
// Maybe you have other shared controller logic..
}
public class HomeController : BaseController
{
// Add endpoints (GET / POST) for Home controller
}
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);
}
}
Ответ 14
Это был самый чистый способ для меня. В моем App_Start\FilterConfig.cs
файле. Больше не могу запускать сборки релизов.
...
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
if (!Web.HttpContext.Current.IsDebuggingEnabled) {
filters.Add(new RequireHttpsAttribute());
}
...
}
В качестве альтернативы вы можете установить для него только https, когда включена пользовательская страница с ошибкой.
...
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
if (Web.HttpContext.Current.IsCustomErrorEnabled) {
filters.Add(new RequireHttpsAttribute());
}
...
}
Ответ 15
Пожалуйста, обратитесь к этому сообщению Риком Андерсоном на RickAndMSFT на Azure и MVC, заполняющем лазурный зазор
http://blogs.msdn.com/b/rickandy/archive/2011/04/22/better-faster-easier-ssl-testing-for-asp-net-mvc-amp-webforms.aspx