远程需要HTTPS MVC 5

本文关键字:MVC HTTPS 程需 | 更新日期: 2023-09-27 18:29:58

我有以下属性来确保远程站点页面以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站点具有相同的设置和web.config

页面重定向不正确

Firefox检测到服务器正在以一种永远不会完成的方式重定向对此地址的请求。

此问题有时可能是由禁用或拒绝接受cookie引起的。

开发人员网站url如下http://dev.datalab.something.org

生产网站url如下http://datalab.something.org

这是呼叫

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

我在这里错过了什么?

更新1:我的管理员已确认SSL终止已在lad平衡器级别设置。我看过iis网站的设置,没有看到https绑定。我只看到http绑定。他是否也需要设置https绑定?

更新2:@AlexeiLevenkov为我指明了正确的方向,这篇文章有我使用的代码,它正在发挥作用。将代码移动到一个单独的答案中。

远程需要HTTPS MVC 5

您的站点位于执行SSL终止的负载均衡器之后,因此,无论用户看到什么,站点的所有传入流量都是HTTP。这导致您的代码总是试图重定向到HTTPS版本,从而导致无限循环。

修复选项:

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

如何调查此类问题:

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

并不是说我反对编写漂亮的自定义属性,也许在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>

如@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);
        }
    }