Access-Control-Allow-Origin CORS 和 HTTPS 重定向问题 (IIS / IISNode / NodeJS)
Access-Control-Allow-Origin issue with CORS and HTTPS redirect (IIS / IISNode / NodeJS)
我有一个分为两部分的网络应用程序:
- 基于Angular的"front-end"(在Chrome下)运行宁在localhost:8000
- "back-end"基于ExpressJS/NodeJS,运行宁在localhost:3000
在尝试逐步将应用程序转换为完全使用 HTTPS 时,我认为最好先转换 back-end。我已经构建了它,以便我可以在 back-end.
上切换 enable/disable HTTPS 的功能
我已经将 back-end 设置为 运行:
- 在 IIS 下有两个绑定:http 和 https
- IISNode下的NodeJS应用
当我尝试在本地开发环境中 运行 本地主机下的整个应用程序(前端和 back-end),但使用 HTTP-to-HTTPS 重写(重定向) 规则。之后我在 front-end.
上收到 CORS 错误
简而言之,在我的本地开发环境中,我正在尝试:
在阅读了 CORS 的所有内容数小时后,我根据 this blog post and 调整了 web.config 和 applicationHost.config 以尝试捕获请求来源 header 值.这是他们的样子。
我的 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 . (稍后会详细介绍。)
但是如果我使用这个配置,那么我会在 front-end 中得到以下错误:
XMLHttpRequest cannot load
http://localhost:3000/foo/bar/
Response for preflight is invalid (redirect)
我怀疑这是因为 IIS 处理了重定向,但结果是它正在处理带有无效响应(重定向)的预检检查(HTTP 选项)。但是,根据 , and the answer by @sideshowbarker,让我相信 Chrome 的当前版本 59.0.3071.104 应该能够处理来自 CORS 预检选项请求的 HTTP 重定向响应。
如果我从 applicationHost.config 和 HTTP-to-HTTPS 重写和其他规则以及 web.config 中删除服务器变量,然后添加代码以允许 NodeJS 应用程序处理重定向到 HTTPS,那么修改后的应用程序如下所示:
然后出现未知(NodeJS?IIS?)服务器错误,因为请求被取消:
您可以在 chrome://net-internals/#events 中看到取消,即使请求中的 Origin 和响应中的 Access-Control-Allow-Origin headers匹配:
没有有用的错误消息(即使客户端收到了错误消息)这让我相信是 IIS 而不是 NodeJS 取消了请求并且没有发回任何有用的信息。
我最终添加了一个 "null" 条目来处理 CORS 运行 在 NodeJS Lite Server 下(而不是作为实验的 IIS),但我需要这个来 运行在 IIS/IISNode 下。但是,当时的 IIS / IISNode / NodeJS 组合似乎有问题。
我怀疑 "null" 的 Request Origin 很可能是请求的结果,服务器执行重定向,因为您确实有两个请求:
- 来自浏览器的原始请求
- 作为重定向结果的请求
当重定向发生时,我假设重定向请求中的来源与原始 URL 不同,并且由于 https://www.w3.org/TR/cors/#generic-cross-origin-request-algorithms 中所述的原因,来源请求 header 在重定向为空。
但是,这并不能解释为什么,当我让 NodeJS 处理重定向时,Origin 请求和 Access-Control-Allow-Origin 响应 header 值都是 null,并且请求仍然被取消。 :-/
最后,如果我消除了 HTTP-to-HTTPS 重定向的任何尝试,那么应用程序在我的开发环境中可以正常工作。
对预检 OPTIONS
本身的响应必须始终是 2xx 响应——例如 200 或 204。对预检本身的响应永远不能是重定向——例如 302。规范禁止这样做。
但是问题中引用的响应显示服务器响应了这样的重定向;因此引用了错误消息。如果浏览器收到对预检 OPTIONS 请求的 3xx 响应,则规范要求浏览器就此停止——将预检视为失败。
所以唯一的解决办法是修复服务器,这样它就不会为 OPTIONS
提供重定向响应。
的问题中描述了重定向和预检的不同场景。在那种情况下,会发生以下情况:
OPTIONS /documents/123 --> 204 (everything okay, please proceed)
GET /documents/123 --> 303 redirect to `/documents/abc`
也就是说,对 OPTIONS
请求本身的响应是 2xx 成功,但对前端代码发送的后续实际请求的响应是 3xx。
对于这种情况,规范 之前 要求浏览器停止并且不允许发出请求的前端代码访问响应,并且浏览器会响应错误:
The request was redirected to 'http://localhost:3000/foo/bar/
',
which is disallowed for cross-origin requests that require preflight.
请注意,这与问题中针对场景引用的错误消息不同,后者是:
XMLHttpRequest cannot load http://localhost:3000/foo/bar/
Response
for preflight is invalid (redirect)
在那种情况下(问题中的那个),情况是这样的:
OPTIONS /documents/123 --> 307 redirect to `/documents/abc`
即对OPTIONS
的响应是3xx。规范仍然需要预检失败。
但是其他 disallowed for cross-origin requests that require preflight 案例不再需要失败,浏览器中不应再出现该错误; the spec changed in August 2016 to no longer require it,所有浏览器引擎随后都会更新以匹配该更改。
Chrome 是最后一个实现更新的浏览器,但是 it was fixed in the Chrome sources in December 2016,修复在 Chrome 57.
中发布
我有一个分为两部分的网络应用程序:
- 基于Angular的"front-end"(在Chrome下)运行宁在localhost:8000
- "back-end"基于ExpressJS/NodeJS,运行宁在localhost:3000
在尝试逐步将应用程序转换为完全使用 HTTPS 时,我认为最好先转换 back-end。我已经构建了它,以便我可以在 back-end.
上切换 enable/disable HTTPS 的功能我已经将 back-end 设置为 运行:
- 在 IIS 下有两个绑定:http 和 https
- IISNode下的NodeJS应用
当我尝试在本地开发环境中 运行 本地主机下的整个应用程序(前端和 back-end),但使用 HTTP-to-HTTPS 重写(重定向) 规则。之后我在 front-end.
上收到 CORS 错误简而言之,在我的本地开发环境中,我正在尝试:
在阅读了 CORS 的所有内容数小时后,我根据 this blog post and
我的 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 . (稍后会详细介绍。)
但是如果我使用这个配置,那么我会在 front-end 中得到以下错误:
XMLHttpRequest cannot load http://localhost:3000/foo/bar/ Response for preflight is invalid (redirect)
我怀疑这是因为 IIS 处理了重定向,但结果是它正在处理带有无效响应(重定向)的预检检查(HTTP 选项)。但是,根据
如果我从 applicationHost.config 和 HTTP-to-HTTPS 重写和其他规则以及 web.config 中删除服务器变量,然后添加代码以允许 NodeJS 应用程序处理重定向到 HTTPS,那么修改后的应用程序如下所示:
然后出现未知(NodeJS?IIS?)服务器错误,因为请求被取消:
您可以在 chrome://net-internals/#events 中看到取消,即使请求中的 Origin 和响应中的 Access-Control-Allow-Origin headers匹配:
没有有用的错误消息(即使客户端收到了错误消息)这让我相信是 IIS 而不是 NodeJS 取消了请求并且没有发回任何有用的信息。
我最终添加了一个 "null" 条目来处理 CORS 运行 在 NodeJS Lite Server 下(而不是作为实验的 IIS),但我需要这个来 运行在 IIS/IISNode 下。但是,当时的 IIS / IISNode / NodeJS 组合似乎有问题。
我怀疑 "null" 的 Request Origin 很可能是请求的结果,服务器执行重定向,因为您确实有两个请求: - 来自浏览器的原始请求 - 作为重定向结果的请求 当重定向发生时,我假设重定向请求中的来源与原始 URL 不同,并且由于 https://www.w3.org/TR/cors/#generic-cross-origin-request-algorithms 中所述的原因,来源请求 header 在重定向为空。
但是,这并不能解释为什么,当我让 NodeJS 处理重定向时,Origin 请求和 Access-Control-Allow-Origin 响应 header 值都是 null,并且请求仍然被取消。 :-/
最后,如果我消除了 HTTP-to-HTTPS 重定向的任何尝试,那么应用程序在我的开发环境中可以正常工作。
对预检 OPTIONS
本身的响应必须始终是 2xx 响应——例如 200 或 204。对预检本身的响应永远不能是重定向——例如 302。规范禁止这样做。
但是问题中引用的响应显示服务器响应了这样的重定向;因此引用了错误消息。如果浏览器收到对预检 OPTIONS 请求的 3xx 响应,则规范要求浏览器就此停止——将预检视为失败。
所以唯一的解决办法是修复服务器,这样它就不会为 OPTIONS
提供重定向响应。
OPTIONS /documents/123 --> 204 (everything okay, please proceed)
GET /documents/123 --> 303 redirect to `/documents/abc`
也就是说,对 OPTIONS
请求本身的响应是 2xx 成功,但对前端代码发送的后续实际请求的响应是 3xx。
对于这种情况,规范 之前 要求浏览器停止并且不允许发出请求的前端代码访问响应,并且浏览器会响应错误:
The request was redirected to '
http://localhost:3000/foo/bar/
', which is disallowed for cross-origin requests that require preflight.
请注意,这与问题中针对场景引用的错误消息不同,后者是:
XMLHttpRequest cannot load
http://localhost:3000/foo/bar/
Response for preflight is invalid (redirect)
在那种情况下(问题中的那个),情况是这样的:
OPTIONS /documents/123 --> 307 redirect to `/documents/abc`
即对OPTIONS
的响应是3xx。规范仍然需要预检失败。
但是其他 disallowed for cross-origin requests that require preflight 案例不再需要失败,浏览器中不应再出现该错误; the spec changed in August 2016 to no longer require it,所有浏览器引擎随后都会更新以匹配该更改。
Chrome 是最后一个实现更新的浏览器,但是 it was fixed in the Chrome sources in December 2016,修复在 Chrome 57.
中发布