So I've found the [RequiresHttps] attribute but once your in https your kind of stuck there, so to try and be able to have actions on a single url (and scheme) I've found I've ended up having to create my own ExtendedController to revert back to http for actions that don't use [RequireHttps].
Just wondering if what I'm doing is okay or if there is a better way?
public class ExtendedController : Controller
{
protected virtual void HandleHttpRequest(AuthorizationContext filterContext)
{
if (!string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("Cannot post between https and http.");
}
string url = "http://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
filte开发者_高级运维rContext.Result = new RedirectResult(url);
}
protected override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
object[] attributes = filterContext.ActionDescriptor.GetCustomAttributes(true);
if (!attributes.Any(a => a is RequireHttpsAttribute))
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (filterContext.HttpContext.Request.IsSecureConnection)
{
this.HandleHttpRequest(filterContext);
}
}
}
}
What you have is syntatically correct, however a suggestion is to create a new Action filter that inherits from the default RequireHttpsAttribute and takes a parameter to switch between http and https.
public class RequireHttpsAttribute : System.Web.Mvc.RequireHttpsAttribute
{
public bool RequireSecure = false;
public override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
{
if (RequireSecure)
{
base.OnAuthorization(filterContext);
}
else
{
// non secure requested
if (filterContext.HttpContext.Request.IsSecureConnection)
{
HandleNonHttpRequest(filterContext);
}
}
}
protected virtual void HandleNonHttpRequest(AuthorizationContext filterContext)
{
if (String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
// redirect to HTTP version of page
string url = "http://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
filterContext.Result = new RedirectResult(url);
}
}
}
Then, on your action method or controller you would use:
[RequireHttps (RequireSecure = true)]
...
or
[RequireHttps (RequireSecure = false)]
To make it little more manageable. This solution assumes that majority of your web application use HTTP scheme.
Create new action filter RequiresHttp (use HTTP if NeedSsl attribute is not apply explicitly on action or controller),
public override void OnActionExecuting(ActionExecutingContext filterContext) { HttpRequestBase req = filterContext.HttpContext.Request; HttpResponseBase res = filterContext.HttpContext.Response; bool needSsl = filterContext.ActionDescriptor.IsDefined(typeof(NeedSslAttribute), true) || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(NeedSslAttribute), true); if (needSsl && !req.IsSecureConnection) //https: secure { var builder = new UriBuilder(req.Url) { Scheme = Uri.UriSchemeHttps, Port = 444 }; res.Redirect(builder.Uri.ToString()); } else if (!needSsl && req.IsSecureConnection) //http: non secure { var builder = new UriBuilder(req.Url) { Scheme = Uri.UriSchemeHttp, Port = 8081 }; res.Redirect(builder.Uri.ToString()); } base.OnActionExecuting(filterContext); }
And new blank attribute NeedSSL (for indication purpose)
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public sealed class NeedSslAttribute : Attribute { }
Apply RequiresHttp as global action filter in Global.aspx.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new RequiresHttp()); }
Now apply apply NeedSslAttribute on controllers and actions where do you want to use HTTPS scheme
[NeedSsl] [AllowAnonymous] public ActionResult LogOn()
This code is not perfect as action filter RequiresHttp
does multiple jobs i.e. check NeedSsl
attribute and apply HTTP
or HTTPS
scheme. Would have been better if we could use two action filters RequiresHTTP
and RequiresHTTPS
.
Now if RequiresHTTP
was set as global filter and RequiresHTTPS
filter was applied on specific actions and specific RequiresHTTPS
filter would have given preference.
精彩评论