我想使用RequireHttpsAttribute来阻止将不安全的HTTP请求发送到操作方法。
C#
[RequireHttps] //apply to all actions in controller
public class SomeController
{
[RequireHttps] //apply to this action only
public ActionResult SomeAction()
{
...
}
}
VB
<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 Development Server上的开发工作站上运行时却不能使用?
答案 0 :(得分:129)
如果您在开发工作站上运行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()
{
}
}
在Visual Basic中,属性在技术上与它们应用的定义属于同一行。你不能把条件编译语句放在一行中,所以你被迫写两次函数声明 - 一次使用属性,一次不使用。但是,如果你不介意丑陋,它确实有效。
#If Not Debug Then
<RequireHttps()> _
Function SomeAction() As ActionResult
#Else
Function SomeAction() As ActionResult
#End If
...
End Function
有些人提到从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
基本上,如果当前请求是本地请求(即,您通过localhost访问该站点),则新属性将退出而不是运行默认的SSL授权代码。您可以像这样使用它:
<RemoteRequireHttps()> _
Public Class SomeController
<RemoteRequireHttps()> _
Public Function SomeAction() As ActionResult
...
End Function
End Class
更干净!如果我未经测试的代码确实有效。
答案 1 :(得分:65)
如果有人需要C#版本:
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);
}
}
}
答案 2 :(得分:26)
从RequireHttps派生是一种很好的方法。
要完全解决问题,您也可以在本地计算机上使用带有自签名证书的IIS。 IIS比内置的Web服务器更快,您的优势在于您的开发环境更像是生产环境。
答案 3 :(得分:11)
利用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());
}
}
答案 4 :(得分:10)
由于ASP.Net开发服务器首先引起了您的问题,因此值得注意的是Microsoft现在有IIS Express,它随Visual Studio一起提供(自VS2010 SP1以来)。这是IIS的简化版本,与开发服务器一样易于使用,但支持IIS 7.5的完整功能集,包括SSL。
Scott Hanselman在working with SSL in IIS Express上有详细的帖子。
答案 5 :(得分:9)
如何在自定义属性中继承RequireHttps属性。然后,在自定义属性中,检查当前请求的IsLocal属性,以查看请求是否来自本地计算机。如果是,则不应用基本功能。否则,调用基本操作。
答案 6 :(得分:4)
这对我有用, MVC 6(ASP.NET Core 1.0)。 代码检查调试是否正在开发中,如果没有,则不需要ssl。 所有修改都在 Startup.cs 。
中添加:
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));
}
});
}
答案 7 :(得分:3)
如果你可以派生和覆盖 - 去做。如果你不能 - MVC附带源代码,只需获取源代码并创建自己的[ForceHttps]属性来检查IsLocal。
答案 8 :(得分:3)
对于MVC 3,我添加了自己的FilterProvider(基于此处的代码:Global and Conditional Filters,其中包括(显示本地用户的调试信息等)将在RequireHttpsAttribute
时用HttpContext.Request.IsLocal == false
装饰所有操作{1}}。
答案 9 :(得分:3)
在研究了aroud之后,我能够用IIS Express解决这个问题并覆盖Controller类的OnAuthorization方法(Ref#1)。我也选择了Hanselman推荐的路线(参考文献#2)。但是,由于两个原因,我对这两种解决方案并不完全满意: 1. Ref#1的OnAuthorization仅适用于动作级别,而不适用于控制器级别 2. Ref#2需要大量的设置(用于makecert的Win7 SDK),netsh命令,并且为了使用端口80和端口443,我需要以管理员身份启动VS2010,我不赞成。
所以,我提出了这个解决方案,它注重简单性,具备以下条件:
我希望能够在Controller类或操作级别使用RequireHttps attbbute
我希望MVC在RequireHttps属性存在时使用HTTPS,如果不存在则使用HTTP
我不想以管理员身份运行Visual Studio
我希望能够使用IIS Express分配的任何HTTP和HTTPS端口(参见注释#1)
我可以重用IIS Express的自签名SSL证书,如果看到无效的SSL提示,我不在乎
我希望dev,test和production具有完全相同的代码库和相同的二进制文件,并且与其他设置无关(例如,使用netsh,mmc cert snap-in等)
现在,有了背景和解释,我希望这段代码可以帮助别人并节省一些时间。基本上,创建一个继承自Controller的BaseController类,并从此基类派生控制器类。既然你已经读过这篇文章了,我想你知道怎么做。所以,快乐的编码!
注意#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 :(得分:3)
您可以在生产和开发工作站上使用的一种解决方案。它基于您在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);
}
}
}
}
不要忘记在AccountController中装饰 LogOn 方法
[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 :(得分:2)
正如Joel所说,你可以使用#if !DEBUG
指令改变编译。
我刚刚发现您可以在web.config文件编译元素中更改DEBUG符号的值。希望有所帮助。
答案 12 :(得分:1)
MVC 6 (ASP.NET Core 1.0):
正确的解决方案是使用env.IsProduction()或env.IsDevelopment()。在how to require https only in production上阅读有关此答案背后的原因的更多信息。
以下简明回答(请参阅上面的链接,了解有关设计决策的更多信息),有两种不同的风格:
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);
}
}
答案 13 :(得分:1)
这对我来说是最干净的方式。在我的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());
}
...
}
答案 14 :(得分:-1)
请参阅Rick Anderson关于Azure和RickAndMSFT的这篇文章。 MVC填补Azure差距