【问题标题】:Access-Control-Allow-Origin issue with CORS and HTTPS redirect (IIS / IISNode / NodeJS)CORS 和 HTTPS 重定向的 Access-Control-Allow-Origin 问题(IIS / IISNode / NodeJS)
【发布时间】:2017-11-21 00:40:22
【问题描述】:

我有一个分为两部分的 Web 应用程序:

  • 基于 Angular(在 Chrome 下)运行在 localhost:8000 上的“前端”
  • 基于 ExpressJS/NodeJS 的“后端”,运行在 localhost:3000

在尝试逐步将应用程序转换为完全使用 HTTPS 时,我认为首先转换后端会更好。我已经构建了它,以便我可以在后端切换启用/禁用 HTTPS 的能力。

我已经设置后端运行:

  • 在 IIS 下有两个绑定:http 和 https
  • IISNode 下的 NodeJS 应用程序。

当我尝试在本地开发环境中的 localhost 下运行整个应用程序(前端和后端)时出现问题,但使用 HTTP 到 HTTPS 重写(重定向)规则。之后我在前端收到 CORS 错误。

简而言之,在我的本地开发环境中,我正在尝试:

在阅读了 CORS 的所有内容几个小时后,我根据 this blog postthis StackOverflow article 调整了 web.config 和 applicationHost.config,以尝试捕获 Request Origin 标头值。这是它们的样子。

我的 applicationHost.config 包含此部分:

<location path="Default Web Site">
    <system.webServer>
        <rewrite>
            <allowedServerVariables>
                <add name="CAPTURED_ORIGIN" />
                <add name="RESPONSE_Access-Control-Allow-Origin" />
            </allowedServerVariables>
        </rewrite>
    </system.webServer>
</location>

这是我的Web.config

<?xml version="1.0" encoding="utf-8"?>
<!--
     This configuration file is required if iisnode is used to run node processes behind
     IIS or IIS Express.  For more information, visit:

     https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config
-->

<configuration>
  <system.webServer>
    <handlers>

      <!-- Indicates that the server.js file is a node.js site to be handled by the iisnode module -->
      <add name="iisnode" path="bin/www" verb="*" modules="iisnode" />
    </handlers>   
    <rewrite>
      <rules>
        <rule name="Capture Origin Header"> 
            <match url=".*" /> 
            <conditions> 
              <add input="{HTTP_ORIGIN}" pattern=".+" /> 
            </conditions> 
            <serverVariables> 
              <set name="CAPTURED_ORIGIN" value="{C:0}" /> 
            </serverVariables> 
            <action type="None" /> 
        </rule>

        <!-- HTTP-to-HTTPS redirect -->
                <rule name="Redirect to HTTPS" enabled="true" stopProcessing="true">
                    <match url="(.*)" ignoreCase="true" />
            <conditions logicalGrouping="MatchAll" trackAllCaptures="true">
                        <add input="{HTTPS}" pattern="^off$" />
                        <add input="{HTTP_HOST}" pattern="([^/:]*?):[^/]*?" />
            </conditions>                   
                      <action type="Redirect" url="https://{C:1}:3443/{R:0}" appendQueryString="true" redirectType="Temporary" />
                </rule>

        <!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
        <rule name="StaticContent">
          <action type="Rewrite" url="public{REQUEST_URI}" />
        </rule>

        <!-- All other URLs are mapped to the node.js site entry point -->
        <rule name="DynamicContent">
          <conditions>
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True" />
          </conditions>
          <action type="Rewrite" url="bin/www" />
        </rule>
      </rules>
      <outboundRules> 
        <rule name="Set-Access-Control-Allow-Origin for known origins"> 
          <match serverVariable="RESPONSE_Access-Control-Allow-Origin" pattern=".+" negate="true" /> 
          <action type="Rewrite" value="{CAPTURED_ORIGIN}" />
        </rule> 
      </outboundRules>       
    </rewrite>

    <!-- Make sure error responses are left untouched -->
    <httpErrors existingResponse="PassThrough" />

  </system.webServer>
  <appSettings>
    <add key="HTTPS_ENABLED" value="true" />
  </appSettings>
</configuration>

NodeJS 应用程序还设置为处理来自 localhost:8000、localhost:3000、localhost:3443(本地 https)和“null”(转换为字符串)的 CORS。 (稍后会详细介绍。)

但是如果我使用这个配置,那么我会在前端得到以下错误:

XMLHttpRequest 无法加载 http://localhost:3000/foo/bar/ 预检响应无效(重定向)

我怀疑这是因为 IIS 处理了重定向,但结果它正在处理带有无效响应(重定向)的预检检查(HTTP OPTIONS)。但是,根据this StackOverflow article@sideshowbarker 的回答,我相信当前版本的 Chrome 59.0.3071.104 应该能够处理来自 CORS 预检 OPTIONS 请求的 HTTP 重定向响应。

如果我从 applicationHost.config 和 HTTP-to-HTTPS 重写和其他规则以及 web.config 中删除服务器变量,然后添加代码以允许 NodeJS 应用程序处理重定向到 HTTPS,那么修改后的应用程序如下所示:

然后出现unknown (NodeJS? IIS?) server error 因为请求被取消了:

即使请求中的 Origin 和响应标头中的 Access-Control-Allow-Origin 匹配,您也可以在 chrome://net-internals/#events 中看到取消:

没有有用的错误消息(即使客户端正在接收),这让我相信是 IIS 而不是 NodeJS 取消了请求并且没有发回任何有用的信息。

当在 NodeJS Lite 服务器(而不是 IIS 作为实验)下运行时,我最终添加了一个“null”条目来处理 CORS,但我需要它在 IIS/IISNode 下运行。但是,当时的 IIS / IISNode / NodeJS 组合似乎有问题。

我怀疑“null”的请求来源很可能是服务器执行重定向的请求的结果,因为您确实有两个请求: - 来自浏览器的原始请求 - 作为重定向结果的请求 当重定向发生时,我假设重定向请求中的来源与原始 URL 不同,并且由于https://www.w3.org/TR/cors/#generic-cross-origin-request-algorithms 中所述的原因,重定向中的 Origin 请求标头为空。

但是,这并不能解释为什么,当我让 NodeJS 处理重定向时,Origin 请求和 Access-Control-Allow-Origin 响应标头值都为 null 并且请求仍然被取消。 :-/

最后,如果我消除了任何 HTTP 到 HTTPS 重定向的尝试,那么应用程序在我的开发环境中可以正常工作。

【问题讨论】:

  • stackoverflow.com/questions/42168773/… 是另一种情况。预检 OPTIONS 本身的响应必须始终是 20 倍成功响应——例如,200 或 204。对预检本身的响应永远不能是重定向——302 或其他。规范禁止这样做。但是您发出请求的服务器正在响应这样的重定向;因此您看到的错误消息。因此,唯一的解决方案是修复服务器,这样您就不会对该 OPTIONS 请求进行重定向响应。
  • @sideshowbarker 我想知道你是否会出现。你以前的回答的忠实粉丝。 :-) 那么,在使用 HTTP 到 HTTPS 重写规则时,某些 Web 服务器 (IIS) 似乎被破坏了?这是我不明白的部分。使用 IIS 重写规则,我的理解是浏览器没有立即响应,而是服务器重定向然后提供响应。这就是我感到困惑的地方。您是说在尝试处理 CORS 预检请求时不能使用 HTTPS 重定向规则?
  • @sideshowbarker - 您在stackoverflow.com/questions/42168773/… 中的回复也表明“规范中不再限制浏览器不遵循 CORS 预检重定向,但浏览器需要更新其实现以匹配规范更改”这就是让我相信 3xx 回复没问题的原因
  • 是的,这有点令人困惑,我应该更准确地措辞该陈述,但您遇到的情况与其他问题中描述的情况不同。归根结底,服务器永远不应该使用 3xx 响应 OPTIONS 请求。为什么有些我不知道,你可以做些什么改变让这个特定的服务器不让它响应 3xx 我也不知道。但底线是,如果浏览器收到对预检 OPTIONS 请求的 3xx 响应,规范要求(仍然)浏览器停在那里——认为预检失败。
  • @sideshowbarker - 关于通过 NodeJS 路由的第二种情况的任何想法?在这种情况下,NodeJS 在 HTTPS 重定向之前在所有情况下都返回 200。但是,在这种情况下,请求会被“取消”。

标签: node.js redirect iis cors iisnode


【解决方案1】:

对预检OPTIONS 本身的响应必须始终是 2xx 成功响应,例如 200 或 204。对预检本身的响应永远不能是重定向,例如 302。规范禁止这样做。

但问题中引用的响应显示服务器使用此类重定向进行响应;因此引用的错误消息。如果浏览器收到对预检 OPTIONS 请求的 3xx 响应,则规范要求浏览器在此停止——将预检视为失败。

因此,唯一的解决方案是修复服务器,使其不会为 OPTIONS 提供重定向响应。


CORS request with Preflight and redirect: disallowed. Workarounds? 的问题中描述了重定向和预检的不同场景。在那种情况下,会发生这种情况:

OPTIONS /documents/123   --> 204 (everything okay, please proceed)
GET /documents/123       --> 303 redirect to `/documents/abc`

即对OPTIONS请求本身的响应是2xx成功,但是对前端代码发送的后续实际请求的响应是3xx。

对于这种情况,规范以前要求浏览器停止并且不允许发出请求的前端代码访问响应,并且浏览器会响应如下错误:

请求被重定向到'http://localhost:3000/foo/bar/', 对于需要预检的跨域请求,这是不允许的。

请注意,这与为问题中的场景引用的错误消息不同,即:

XMLHttpRequest 无法加载 http://localhost:3000/foo/bar/ 响应 预检无效(重定向)

在那种情况下(问题中的那个),反而会发生这种情况:

OPTIONS /documents/123   --> 307 redirect to `/documents/abc`

即对OPTIONS本身的响应是3xx。该规范仍然需要为此进行预检失败。

但是对于其他不允许需要预检的跨域请求的情况,规范不再需要失败,并且浏览器中不应再出现该错误; the spec changed in August 2016 to no longer require it,所有浏览器引擎随后更新以匹配该更改。

Chrome 是最后一个实施更新的浏览器,但 it was fixed in the Chrome sources in December 2016,并且在 Chrome 57 中提供了修复。

【讨论】:

    猜你喜欢
    • 2017-10-24
    • 2021-05-12
    • 2016-09-20
    • 2016-09-30
    • 2019-02-06
    • 1970-01-01
    • 2019-06-17
    • 2017-06-21
    • 2017-12-11
    相关资源
    最近更新 更多