这学期的Object Oriented Design课程要求以组为单位做一个项目,我们的项目前端使用Vuejs,后端使用Spring Boot,通过REST API通信。 有一天前端的朋友告诉我她在调试的时候遇到了一个问题。她在本地运行Vue项目,连接位于AWS上的API。这是一个跨域请求,浏览器会首先发送一个Preflight请求来预检。奇怪的是虽然Preflight请求正常返回了200,但是后面的请求却并没有继续进行。 * Preflight request 术语表 | MDN 我试图在我的电脑上复现这个问题,然而我的电脑上却一切正常,完全不能重现。 经过各种尝试之后找到了一个突破口——出现问题的这个接口是通过Cloudflare和nginx转发的,而换成直接从Docker暴露出来的8080端口则一切正常。 分析请求 第一步,先来对比成功的和失败的请求的header中的属性。稳定版Chrome默认是不显示status为200的Preflight请求的,需要Canary版Chrome才行。 我按照朋友给我发来的截图通过cURL构造出请求,并从我自己的Chrome调试器中将成功的请求导出为cURL。分别运行一次,结果如图: 左侧的是失败的请求,右侧是成功的请求。 发现原因在于失败的请求的响应头中没有包含access-control-allow-origin和access-control-allow-method这两个属性(图中右下刷红),所以这个Preflight请求所得到的结果相当于是无效的。 定位问题 接下来我们需要定位问题所在,我的服务器上的环境如下: 但是为了方便调试,其中每一个节点都是可以从公网访问的,因此我将其划分为三个场景,分别进行测试,下文中提到的“场景x”均对应下表中的编号。 场景: 编号 入口 upstream 1 https://api.app.com cloudflare:443->nginx:80>docker:8080->spring boot:8080 2 http://aws.app.com nginx:80>docker:8080->spring boot:8080 3 http://aws.app.com:8080 docker:8080->spring boot:8080 我使用cURL分别测试三种场景,结果如图 这样我们可以将问题定位到nginx。 但是回顾上面的第一次测试,我们可以发现nginx并不是影响问题的唯一变量。另一个变量是请求的origin和referer。 排除掉干扰因素Cloudflare和Docker后,我们将这个问题的变量控制在两个:请求是否经过nginx,以及客户端端口号是否为8080。 得到如下表格: 经过nginx 不经过nginx origin端口为8080 ❌ ✅ origin端口为8081 ✅ ✅ 分析nginx 我们着重分析经过nginx的这两个场景。 首先分析从浏览器到nginx的这段路程。 浏览器发起CORS […]