by chiyiw on 2016/12/8
a.com/index.html中的app.js中发起$.getJSON('b.com/data.json')
a.com:8080/index.html中的app.js发起对a.com:8000下资源的请求
以下情况不会发生跨域问题:
$.getJSON('a.com/data.json')
,也就是说app.js是从哪儿来得无所谓,关键看的是app.js中的请求目标和当前页面是否在同域下资源部署如下:
动态资源 ->localhost:8080: index.html
静态资源 ->localhost:8000: app.js, app.css, bg.png, data.json
<html>
<head>
<meta charset="UTF-8">
<link href="//localhost:8000/app.css" type="text/css" rel="stylesheet">
</head>
<body>
<script src="//localhost:8000/app.js"></script>
</body>
</html>
这是一个html页面,它部署在localhost:8080下,页面中请求了一个app.css和一个app.js, 这两个文件在localhost:8000下,属于不同域。
页面能够正确加载这两个文件,说明html没有跨域限制。
body { background-image: url("./bg.png");} // 设置页面背景图片
@font-face {
font-family: 'MyFont';
src: url('./script.ttf');
}
body { font-family: 'MyFont'; }
从app.css中加载了一张图片,这张图片在localhost:8000下,而当前页面是localhost:8080/index.html, 属于不同域,可以正确加载。
但是加载的字体出现跨域问题,报错如下:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.
这个实验说明对于图片css没有跨域限制,但是对字体文件存在。
$(function () {
$.getJSON('//localhost:8000/data.json', function (data) { // 此处使用'./data.json'无法访问
console.log(data)
})
})
在app.js中发起一个请求,请求localhost:8000下的data.json,由于当前页面在localhost:8080下,属于不同域。 data.json无法加载成功,浏览器报如下错误:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.
这是典型的跨域错误。
而解决跨域问题,也是从这个报错信息开始的,错误显示请求的资源的Response的header中没有’Access-Control-Allow-Origin’这个值,当前源’http://localhost:8080’的请求无法得到许可。这个信息初看有些茫然,好像是说,服务端返回了Response, 但是Response中header没有给本域名权限。
CORS(Cross-origin resource sharing)协议是现代浏览器都支持的方式,它定义了跨域的规范,其实真实的跨域处理是两次请求(图来自https://www.html5rocks.com/static/images/cors_flow.png)。
浏览器在收到js的请求时,判断request中的当前页面与url是否属于同一个域
如果属于不同的域,浏览器在request的头部加上’Origin’属性,值为当前页面的域,比如http://localhost:8080, 然后发起一次OPTIONS请求给url, OPTIONS中只包含request的头信息, 目标服务器返回response, 浏览器检查response的header中是否有’Access-Control-Allow-Origin’且与发送的’Origin’相匹配,如果匹配,才发送真正的请求过去,将真正的响应呈现给客户端。
如果服务器端在OPTIONS的请求中没有返回’Access-Control-Allow-Origin’, 则浏览器报上面说到的错误。
了解了跨域限制出现的原因,就可以容易解决问题了。
在静态资源服务器中,向response的header添加以下属性即可:
'Access-Control-Allow-Origin: *'
这种配置允许任意来源的js请求获取到该资源,对于一些公共的资源服务器,这样配置是没有问题的。但是有些资源是私有的,为了达到安全的控制,这里应该配置具体的来源域名:
'Access-Control-Allow-Origin: http://localhost:8080, http://localhost:8088'
可以通过逗号对多个值进行分隔。
其次,为了更加具体的实现安全控制,还可以配置如下头信息:
Access-Control-Allow-Methods: GET, POST, PUT // 限定请求方式
Access-Control-Max-Age: 1800 // 限定跨域允许时间(1800s=3min)
Access-Control-Allow-Headers: X-Custom-Header // 限定实际请求中可以包含的头信息
Access-Control-Allow-Credentials: true // 是否允许携带cookie, 默认为false
同时,利用浏览器的第一次询问,服务端还可以使用Access-Control-Allow-Credentials: true
允许浏览器在跨域请求的时候携带cookie,更多CORS的详细用法,可以看这篇文章:https://www.html5rocks.com/en/tutorials/cors/#toc-making-a-cors-request
bg.png的Referer是http://localhost:8000/app.css,解释了为什么可以通过'./bg.png'
访问。
app.json的Referer是http://localhost:8080/index.html,而不是app.js, 因此通过'./app.json'
无法访问,因为它代表//localhost:8080/app.json
, 而通过'//localhost:8000/app.json'
会出现跨域的问题。
结论:js中的请求无论是自动发起的还是手动发起的,其Referer都是当前页面,而不是本身js的来源;而css中发起的请求所带的Referer是css本身的来源地址。
跨域既然为开发者带来了诸多问题,它为什么会存在呢?
为了安全。
前端浏览器中运行的js,能从cookie中获得很多的敏感信息,试想在登录了支付宝的浏览器中,浏览器包括了当前的用户的很多私密信息,如果任何一个来源的网页都可以利用其js,向支付宝进行请求操作,就会带来巨大安全隐患。
参考:
探讨跨域请求资源的几种方式, by 木的树