浏览器出于安全问题的考虑,采用了同源策略:不能获取其他域名(包括端口)下面资源的限制 。
(1) Cookie、LocalStorage 和 IndexDB 无法读取。 (2) DOM 无法获得。 (3) AJAX 请求不能发送。(比如运行在127.0.0.1:80的web服务想要通过ajax来获取127.0.0.1:8090的数据) |
这里讨论的是第三条,ajax请求只能发送给同源的网址。:
实际上,跨域请求的检查和拦截者是浏览器。
常用解决方案:
-
CORS:
1. 简单请求:
|
|
浏览器默认在请求头信息中增加Origin字段,跨域后的服务器根据这个值决定是否同意这次请求:
服务器不同意:返回一个正常的HTTP回应,不包含Access-Control-Allow-Origin(注意:状态码可能依旧为200);
服务器同意:在返回请求头中加入Access-Control-Allow-Origin,其他几个相关的头字段:
Access-Control-Allow-Origin: http://XXX (要么是Origin字段的值,要么是*表示接收任意域名)
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
请求带Cookies和HTTP认证信息需要:
前端:ajax请求时设置XMLHttpRequest对象的withCredentials属性为true;
后端:返回请求头中添加 Access-Control-Allow-Credentials: true
2. 预检请求
预检请求为非简单请求(比如使用目前最常用的conent-Type:application/json)。浏览器在检测到请求不属于简单请求,首先使用options方法发起一个预检请求到服务器,获知服务器是否允许该请求。预检请求一般包含这三个请求头:
发送options请求: |
|
服务器允许时的返回: |
|
服务器拒绝时的返回: | 会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段 |
注意:对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin
的值为“*
”。即:发送请求时的XMLHttpRequest对象的withCredentials属性设为true,浏览器期望在请求时携带cookie信息,这时候服务器如果Access-Control-Allow-Origin:*,也被判断为预检请求失败。参考下图:
-
JSONP方法:
将请求url动态拼接成引用外部js文件的方式。
<script>
function fn(data) {
alert(data);
}
</script>
<!--当作js文件来加载。
通常:在2.txt中常调用回调函数:fn([...]),
或在src中定义获取数据后的回调:src="http:...&calllback=fn"-->
<script src="http://127.0.0.1:8887/2.txt">
// 以下的写法会报错,抛弃
// var xhr = new XMLHttpRequest()
// xhr.open('GET', 'http://127.0.0.1:8887/2.txt')
// xhr.send()
</script>
-
webSocket方法
WebSocket是一种通信协议,不实行同源策略。
-
设置代理服务器
比如通过webpack-dev-server(使用的是http-proxy-middleware)实现跨域代理,参考这篇:https://webpack.docschina.org/configuration/dev-server/#devserverproxy
-
浏览器关闭同源策略
如果是开发调试环境,还可以采用关闭chrome浏览器同源策略的方式来暂时方便开发(这时候浏览器不检查是否跨域,也不会发送预检请求)。
方法:在命令行打开浏览器时,浏览器地址后加上后缀:
“--disable-web-security --user-data-dir"
或在快捷方式中右键属性,目标框中chrome浏览器的目录后,加以上后缀且以空格隔开,如下图:
以后打开的chrome浏览器会弹出以下提示,忽略即可
参考自:
http://www.ruanyifeng.com/blog/2016/04/cors.html