【发布时间】: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 post 和 this 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