同源策略
-
浏览器出于安全方面的考虑,只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。
-
同协议,同域名,同端口
-
需要注意的是: 对于当前页面来说页面存放的 JS 文件的域不重要,重要的是加载该 JS的页面所在什么域,即代码的执行环境。对比当前页面的url和ajax请求的url。
-
同源策略限制以下几种行为:
1.) Cookie、LocalStorage 和 IndexDB 无法读取 2.) DOM 和 Js对象无法获得 3.) AJAX 请求不能发送
跨域
-
即绕过同源策略去获取数据
-
广义的跨域:
1.) 资源跳转: A链接、重定向、表单提交
2.) 资源嵌入: <link>、<script>、<img>、<frame>等dom标签,还有样式中background:url()、@font-face()等文件外链
3.) 脚本请求: js发起的ajax请求、dom和js对象的跨域操作等
跨域解决方案
JSONP(JSON with padding)
- HTML 中 script,img标签这样获取资源的标签是没有跨域限制的,比如我们经常引入一个其他域下线上cdn的jQuery。
- 实现:JSONP是通过 script 标签加载数据的方式去获取数据当做 JS 代码来执行。提前在页面上声明一个函数,函数名通过接口传参的方式传给后台,后台解析到函数名后在原始数据上「包裹」这个函数名,发送给前端。换句话说,JSONP 需要对应接口的后端的配合才能实现。
- 缺点:JSONP只能发GET请求,因为本质上script加载资源就是GET
前端实现:
document.querySelector('.show').addEventListener('click',function(){
var script = document.createElement('script');
script.src = 'http://127.0.0.1:8080/getWeather?callback=showData';
document.head.appendChild(script);
document.head.removeChild(script);
})
后端部分代码:
case '/getWeather':
var weather = [
'北京:晴',
'杭州:阴',
'上海:多云'
]
res.setHeader('Content-Type','text/json;charset=utf-8')
if(pathObj.query.callback){
res.end(pathObj.query.callback + '(' + JSON.stringify(weather) + ')')
}
else{
res.end(JSON.stringify(weather))
}
break;
CORS
- CORS 全称是跨域资源共享(Cross-Origin Resource Sharing),是一种 ajax 跨域请求资源的方式,支持现代浏览器,IE支持10以上。
- 实现:当你使用 XMLHttpRequest 发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin; 浏览器判断该响应头中是否包含 Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。所以 CORS 的表象是让你觉得它与同源的 ajax 请求没啥区别,代码完全一样。
- 普通跨域请求只需要服务端设置Access-Control-Allow-Origin即可,前端无须设置;若要带cookie请求:前后端都需要设置。
前端实现:
document.querySelector('.show').addEventListener('click',function(){
var xhr = new XMLHttpRequest();
xhr.open('GET','http://127.0.0.1:8080/getWeather',true);
xhr.onload = function(){
if((xhr.status>=200&&xhr.status<=300)||xhr.status==304) {
showData(JSON.parse(xhr.responseText));
}else{
console.log('error');
}
}
xhr.onerror=function(){
console.log('error');
}
xhr.send();
})
后端部分代码:
case '/getWeather':
var weather = [
'北京:晴',
'杭州:阴',
'上海:多云'
]
res.setHeader('Access-control-Allow-Origin','http://a.com:8080')
res.end(JSON.stringify(weather))
break;
降域: document.domain + iframe跨域
-
仅限主域相同,子域不同的跨域应用场景。
-
实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
postMessage跨域
-
postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:
a.) 页面和其打开的新窗口的数据传递
b.) 多窗口之间消息传递
c.) 页面与嵌套的iframe消息传递
d.) 上面三个场景的跨域数据传递 -
用法:postMessage(data,origin)方法接受两个参数
- data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
- origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
-
window.postMessage() 方法被调用时,会在所有页面脚本执行完毕之后(e.g., 在该方法之后设置的事件、之前设置的timeout 事件,etc.)向目标窗口派发一个
MessageEvent
(一个事件)消息。 该MessageEvent
消息有四个属性需要注意:- message 属性表示该message 的类型
- data 属性为 window.postMessage()的第一个参数
- origin 属性表示调用window.postMessage() 方法时调用页面的当前状态
- source 属性记录调用 window.postMessage() 方法的窗口信息。
以上具体实现见https://github.com/mingzihhh/cross-domain-demo
参考资料:
https://segmentfault.com/a/1190000011145364
MDN文档
网友评论