远程需要HTTPS MVC 5

时间:2014-11-10 17:48:13

标签: c# asp.net-mvc-5

我有以下属性以确保远程站点页面以https模式打开。

public class RemoteRequireHttpsAttribute : RequireHttpsAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentException("Filter Context");
            }

            if (filterContext != null && filterContext.HttpContext != null)
            {
                if (filterContext.HttpContext.Request.IsLocal)
                {
                    return;
                }
                else
                {
                    string val = ConfigurationManager.AppSettings["RequireSSL"].Trim();
                    bool requireSsl = bool.Parse(val);
                    if (!requireSsl)
                    {
                        return;
                    }
                }
            }

            base.OnAuthorization(filterContext);
        }
    }

本地开发现在正常工作,因为我不希望它以https模式打开。

开发站点以https模式打开页面 - 此处没有问题(单个节点)。

我正在设置的生产(负载平衡 - 2个节点)站点在哪里给我跟随错误。请注意,dev和prod站点具有相同的setings和web.config

  

页面未正确重定向

     

Firefox检测到服务器正在以永远无法完成的方式重定向此地址的请求。

     

有时可能会因禁用或拒绝接受Cookie而导致此问题。

开发网站网址就像 http://dev.datalab.something.org

Prod网站的网址就像 http://datalab.something.org

这是电话

[RemoteRequireHttps]
public ActionResult Index(string returnUrl, string error)

我在这里缺少什么?

更新1:我的管理员已确认已在lad balancer evel设置了SSL终止。我查看了iis站点设置,但我没有看到https绑定。我只看到http绑定。他是否还需要设置https绑定?

更新2: @AlexeiLevenkov向我指出了正确的方向,this post有我使用的代码,它正在运行。将代码移动到单独的答案中。

3 个答案:

答案 0 :(得分:2)

您的站点是支持SSL终止的负载均衡器 - 因此,无论用户看到什么,所有到您站点的传入流量都是HTTP。这会导致您的代码始终尝试重定向到HTTPS版本,从而无限循环。

要修复的选项:

  • 通常,SSL终止的负载均衡器将通过自定义标头转发原始IP /协议。 x-forwarded-protox-forwarded-for是用于此目的的常见内容。如果使用这些标头或需要一些其他配置,您可能需要与网络管理员核实
  • 或者,您可以关闭SSL终止,但它会给您的服务器带来额外的负担。
  • 还可以将负载均衡器配置为使用与传入请求相同的协议与服务器通信。

如何调查此类问题:

  • 查看http调试器(如Fiddler),了解您是否在循环中获得30x重定向请求。如果没有重定向 - 可能代码错误。
  • 如果您看到重复重定向,则可能意味着网站没有看到实际的请求信息 - 可能是协议,路径Cookie丢失。
  • 要继续调查,请查看用户和服务器之间的设备(CDN,代理,负载均衡器......) - 每个设备都有机会丢失一些日期或转换协议。

答案 1 :(得分:1)

并不是说我反对编写好的自定义属性,或许在web.config中执行重定向并使用web.config可用的转换将生产部署中的启用值从false更改为true是没有意义的?

<rewrite>
  <rules>
    <rule name="SSL_ENABLED" enabled="false" stopProcessing="true">
      <match url="(.*)" />
      <conditions>
        <add input="{HTTPS}" pattern="^OFF$" />
      </conditions>
      <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" appendQueryString="true" redirectType="Permanent" />
    </rule>
  </rules>
</rewrite>

答案 2 :(得分:1)

如@AlexeiLevenkov所述,将修复程序移到单独的答案中。

public class RemoteRequireHttpsAttribute : RequireHttpsAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentException("Filter Context");
            }

            if(filterContext.HttpContext != null)
            {
                if (filterContext.HttpContext.Request.IsSecureConnection)
                {
                    return;
                }

                var currentUrl = filterContext.HttpContext.Request.Url;
                if (currentUrl.Scheme.Equals(Uri.UriSchemeHttps, StringComparison.CurrentCultureIgnoreCase))
                {
                    return;
                }

                if (string.Equals(filterContext.HttpContext.Request.Headers["X-Forwarded-Proto"], "https", StringComparison.InvariantCultureIgnoreCase))
                {
                    return;
                }

                if (filterContext.HttpContext.Request.IsLocal)
                {
                    return;
                }

                var val = ConfigurationManager.AppSettings["RequireSSL"].Trim();
                var requireSsl = bool.Parse(val);
                if (!requireSsl)
                {
                    return;
                }
            }

            base.OnAuthorization(filterContext);
        }
    }

我也更新了ExitHttps属性。这也有类似的问题......

public class ExitHttpsAttribute : FilterAttribute, IAuthorizationFilter
    {
        public void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentException("Filter Context");
            }

            if (filterContext.HttpContext == null)
            {
                return;
            }

            var isSecure = filterContext.HttpContext.Request.IsSecureConnection;

            var currentUrl = filterContext.HttpContext.Request.Url;
            if (!isSecure && currentUrl.Scheme.Equals(Uri.UriSchemeHttps, StringComparison.CurrentCultureIgnoreCase))
            {
                isSecure = true;
            }

            if (!isSecure && string.Equals(filterContext.HttpContext.Request.Headers["X-Forwarded-Proto"], "https", StringComparison.InvariantCultureIgnoreCase))
            {
                isSecure = true;
            }

            if (isSecure)
            {
                //in these cases keep https
                // abort if a [RequireHttps] attribute is applied to controller or action
                if (filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof (RequireHttpsAttribute), true).Length > 0)
                {
                    isSecure = false;
                }

                if (isSecure && filterContext.ActionDescriptor.GetCustomAttributes(typeof (RequireHttpsAttribute), true).Length > 0)
                {
                    isSecure = false;
                }

                // abort if a [RetainHttps] attribute is applied to controller or action
                if (isSecure && filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof (RetainHttpsAttribute), true).Length > 0)
                {
                    isSecure = false;
                }

                if (isSecure && filterContext.ActionDescriptor.GetCustomAttributes(typeof (RetainHttpsAttribute), true).Length > 0)
                {
                    isSecure = false;
                }

                // abort if it's not a GET request - we don't want to be redirecting on a form post
                if (isSecure && !String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
                {
                    isSecure = false;
                }
            }

            if (!isSecure)
            {
                return;
            }

            // redirect to HTTP
            var url = "http://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
            filterContext.Result = new RedirectResult(url);
        }
    }