Asp.net mvc ASP.NET MVC仅在生产中需要TTP
我想使用防止将不安全的HTTP请求发送到操作方法 C# VBAsp.net mvc ASP.NET MVC仅在生产中需要TTP,asp.net-mvc,visual-studio,ssl,https,Asp.net Mvc,Visual Studio,Ssl,Https,我想使用防止将不安全的HTTP请求发送到操作方法 C# VB _ 公共类控制器 _ 作为ActionResult的公共函数SomeAction() ... 端函数 末级 不幸的是,ASP.NET开发服务器不支持HTTPS 如何使我的ASP.NET MVC应用程序在发布到生产环境时使用RequireHttps,而不是在ASP.NET开发服务器上的开发工作站上运行时使用RequireHttps?如何在自定义属性中继承RequireHttps属性。然后,在自定义属性中,检查当前请求的IsLocal属
_
公共类控制器
_
作为ActionResult的公共函数SomeAction()
...
端函数
末级
不幸的是,ASP.NET开发服务器不支持HTTPS
如何使我的ASP.NET MVC应用程序在发布到生产环境时使用RequireHttps,而不是在ASP.NET开发服务器上的开发工作站上运行时使用RequireHttps?如何在自定义属性中继承RequireHttps属性。然后,在自定义属性中,检查当前请求的IsLocal属性,查看该请求是否来自本地计算机。如果是,则不要应用基本功能。否则,调用基本操作。如果在开发工作站上运行版本构建,这将没有帮助,但条件编译可以完成这项工作
#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()
{
}
}
更新
在VisualBasic中,属性在技术上与它们应用于的定义是同一行的一部分。不能将条件编译语句放在一行中,因此必须编写两次函数声明—一次使用属性,一次不使用属性。不过,如果你不介意丑陋的话,它确实有效
#If Not Debug Then
<RequireHttps()> _
Function SomeAction() As ActionResult
#Else
Function SomeAction() As ActionResult
#End If
...
End Function
基本上,如果当前请求是本地的(即通过localhost访问站点),则新属性将退出,而不是运行默认的SSL授权代码。您可以这样使用它:
<RemoteRequireHttps()> _
Public Class SomeController
<RemoteRequireHttps()> _
Public Function SomeAction() As ActionResult
...
End Function
End Class
_
公共类控制器
_
作为ActionResult的公共函数SomeAction()
...
端函数
末级
干净多了!前提是我的未测试代码确实有效。如果可以派生和重写,请执行。如果不能-MVC附带源代码,只需获取源代码并创建自己的[ForceHttps]属性来检查IsLocal。从RequireHttps派生是一个好方法 若要完全避开此问题,您也可以在本地计算机上使用IIS并使用自签名证书。IIS比内置的Web服务器更快,而且您的开发环境更像是生产环境
由于最初是ASP.Net开发服务器导致了您的问题,因此值得注意的是,Microsoft现在有了,它随Visual Studio一起提供(从VS2010 SP1开始)。这是IIS的精简版本,与开发服务器一样易于使用,但支持IIS 7.5的完整功能集,包括SSL
Scott Hanselman在上有一篇详细的帖子。正如Joel提到的,您可以使用
#if!调试指令
我刚刚发现可以在web.config文件编译元素中更改调试符号的值。希望能有所帮助。对于MVC 3,我添加了自己的FilterProvider(基于此处找到的代码:除其他外(为本地用户显示调试信息等),它将在HttpContext.Request.IsLocal==false时使用requireHttpAttribute
装饰所有操作,如果有人需要C版本:
利用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());
}
}
经过研究,我能够用IIS Express和重写控制器类的OnAuthorization方法(参考1)来解决这个问题。我也采用了Hanselman推荐的路线(参考2)。但是,由于两个原因,我对这两个解决方案并不完全满意:
1.Ref#1的授权仅在操作级别有效,而不在控制器类级别有效
2.参考#2需要大量的设置(用于makecert的Win7 SDK)、netsh命令,并且,为了使用端口80和端口443,我需要以管理员身份启动VS2010,对此我表示反对
因此,我提出了这个解决方案,它在以下条件下注重简单性:
我希望能够在控制器类或操作级别使用RequireHttps attbbute
我希望MVC在RequireHttps属性存在时使用HTTPS,如果不存在,则使用HTTP
我不希望必须以管理员身份运行Visual Studio
我希望能够使用IIS Express分配的任何HTTP和HTTPS端口(请参见注释1)
我可以重用IIS Express的自签名SSL证书,我不在乎是否看到无效的SSL提示
我希望开发、测试和生产具有完全相同的代码库和相同的二进制文件,并且尽可能独立于其他设置(例如,使用netsh、mmc证书管理单元等)
现在,背景和解释已经讲完了,我希望这段代码能帮助一些人并节省一些时间。基本上,创建一个从Controller继承的BaseController类,并从这个基类派生控制器类。既然你已经读了这么多,我想你知道如何做这些。所以,祝你编码愉快
注#1:这是通过使用有用的函数“getConfig”实现的(参见代码)
参考文献1:
参考文献2:
==========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);
}
===================结束代码================
#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)
一种解决方案,您可以在生产工作站和开发工作站上使用。它基于您在web.config中的应用程序设置选项
<appSettings>
<!--Use SSL port 44300 in IIS Express on development workstation-->
<add key="UseSSL" value="44300" />
</appSettings>
别忘了在AccountController中修饰登录方法
[RequireHttpsConditional]
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
在您的登录视图中使用类似的内容,以便通过https发布表单
<% using (Html.BeginFormSecure("LogOn", "Account", new { ReturnUrl = Request.QueryString["ReturnUrl"] }, Request.IsSecureConnection, Request.Url)) { %>
请参阅Rick Anderson在RickAndMSFT上发表的这篇文章,以及Azure和MVC填补Azure空白的文章
MVC 6(ASP.NET Core 1.0):
正确的解决方案是使用env.IsProduction()或env.IsDevelopment()。请阅读关于此答案背后原因的更多信息
下面的简明答案(参见上面的链接阅读mo
<appSettings>
<add key="HttpPort" value="" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
<add key="HttpsPort" value="" xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>
</appSettings>
<appSettings>
<!--Use SSL port 44300 in IIS Express on development workstation-->
<add key="UseSSL" value="44300" />
</appSettings>
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);
}
}
}
}
[RequireHttpsConditional]
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
<% using (Html.BeginFormSecure("LogOn", "Account", new { ReturnUrl = Request.QueryString["ReturnUrl"] }, Request.IsSecureConnection, Request.Url)) { %>
public void ConfigureServices(IServiceCollection services)
{
// TODO: Register other services
services.AddMvc(options =>
{
options.Filters.Add(typeof(RequireHttpsInProductionAttribute));
});
}
[RequireHttpsInProductionAttribute]
public class BaseController : Controller
{
// Maybe you have other shared controller logic..
}
public class HomeController : BaseController
{
// Add endpoints (GET / POST) for Home controller
}
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);
}
}
private IHostingEnvironment CurrentEnvironment { get; set; }
public Startup(IHostingEnvironment env)
{
CurrentEnvironment = env;
}
public void ConfigureServices(IServiceCollection services)
{
// additional services...
services.AddMvc(options =>
{
if (!CurrentEnvironment.IsDevelopment())
{
options.Filters.Add(typeof(RequireHttpsAttribute));
}
});
}
...
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
if (!Web.HttpContext.Current.IsDebuggingEnabled) {
filters.Add(new RequireHttpsAttribute());
}
...
}
...
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
if (Web.HttpContext.Current.IsCustomErrorEnabled) {
filters.Add(new RequireHttpsAttribute());
}
...
}