<meta name="source" content="lake">
出于安全原因,浏览器限制从脚本发起的跨源HTTP请求。例如,XMLHttpRequest和Fetch API遵循同源策略。这意味着,使用这些api的web应用程序只能从加载该应用程序的同一源请求资源,除非来自其他源的响应包含正确的CORS头文件。
同源和非同源请求 图片来源 https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
1 什么是同源策略(Same-origin policy)
简单来说就是一句话: URL中的协议名(scheme)、域名(host)、端口号(port)全部都得相等
<pre class="cm-s-default" style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);"> hierarchical part ┌───────────────────┴─────────────────────┐ authority path ┌───────────────┴───────────────┐┌───┴────┐ abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1 └┬┘ └───────┬───────┘ └────┬────┘ └┬┘ └─────────┬─────────┘ └──┬──┘ scheme user information host port query fragment</pre>
URL
<pre class="cm-s-default" style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">URL 说明 是否允许通信 http://www.domain.com/a.js http://www.domain.com/b.js 同一域名,不同文件或路径 允许 http://www.domain.com/lab/c.js http://www.domain.com:8000/a.js http://www.domain.com/b.js 同一域名,不同端口 不允许 http://www.domain.com/a.js https://www.domain.com/b.js 同一域名,不同协议 不允许 http://www.domain.com/a.js http://192.168.4.12/b.js 域名和域名对应相同ip 不允许 http://www.domain.com/a.js http://x.domain.com/b.js 主域相同,子域不同 不允许 http://domain.com/c.js http://www.domain1.com/a.js http://www.domain2.com/b.js 不同域名 不允许</pre>
常见跨域场景
2 常见跨域解决方案
1、 通过jsonp跨域
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域
3 什么是CORS(Cross-Origin Resource Sharing (CORS))
3.1 什么是CORS
MDN Cross-Origin Resource Sharing (CORS)
阮一峰 跨域资源共享 CORS 详解 关于CORS阮一峰老师写的绝对是经典
3.2 CORS分类
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
3.3 简单请求
1 Simple method: GET, POST or HEAD
2 Simple headers– the only allowed custom headers are:
-
Accept
, -
Accept-Language
, -
Content-Language
, -
Content-Type
with the valueapplication/x-www-form-urlencoded
,multipart/form-data
ortext/plain
.
3.4 非简单请求
除了简单请求都是非简单请求
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询(OPTIONS)请求,称为"预检"请求(preflight)有的场景忽略了预检请求的nginx配置,导致跨域配置失败。
非简单请求 预检请求流程(OPTIONS)
4 nginx配置CORS
- Access-Control-Allow-Origin 如果需要跨域发送cookie,不能配置* (这样会很不安全),需要配置具体的域名或者使用变量 $http_origin'(不安全)
- Access-Control-Allow-Headers 根据实际情况增删
- location 模块 / 代表通配符,所有请求都会走这里
- always 参数 If the always parameter is specified (1.7.5), the header field will be added regardless of the response code. 无论响应代码如何,头字段都将被添加。
<pre class="cm-s-default" style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);"># # Wide-open CORS config for nginx # location / { if (request_method = 'POST') { add_header 'Access-Control-Allow-Origin' '' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; } if ($request_method = 'GET') { add_header 'Access-Control-Allow-Origin' '' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always; add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; } } </pre>
https://enable-cors.org/server_nginx.html
踩坑
nginx: [emerg] "add_header" directive is not allowed here -
没有返回跨域头信息 - 可能只是配置了某个location下面的跨域头配置,可能是返回的状态码有404等状态码,浏览器不认可返回的跨域头配置
预检请求没通过 - 没有处理OPTIONS请求或者,OPTIONS没有返回对应的状态码