同源策略(same origin policy),一种安全策略,用于限制一个源的文档或者它加载的脚本如何能与另一个源的资源进行交互。
浏览器默认两个不同的源之间是可以互相访问资源和操作 DOM 的。两个不同的源之间若是想要访问资源或者操作 DOM,就会受到同源策略的制约。
同源:URL 的协议、主机、端口号相同。
解释一下协议、主机、端口号:
同源策略的限制:
jsonp(JSON with Padding),是 JSON 的一种 “使用模式”,可以让网页跨域读取数据,其本质是利用 script 标签的开放策略,浏览器传递 callback 参数到后端,后端返回数据时会将 callback 参数作为函数名来包裹数据,从而浏览器就可以跨域请求数据并制定函数来自动处理返回数据。
demo:
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参callback给后端,后端返回时执行这个在前端定义的回调函数
script.src = 'http://a.qq.com/index.php?callback=handleCallback';
document.head.appendChild(script);
// 回调执行函数
function handleCallback(res) {
alert(JSON.stringify(res));
}
优点:
缺点:
情景:跨站请求正常发送,但是结果被浏览器拦截。
原因:浏览器将不同域的内容隔离在不同的进程中,网络进程负责下载资源并将其送到渲染进程中,但由于跨域限制,某些资源可能被阻止加载到渲染进程。如果浏览器发现一个跨域响应包含了敏感数据,它可能会阻止脚本访问这些数据,即使网络进程已经获得了这些数据。CORB 的目标是在渲染之前尽早阻止恶意代码获取跨域数据。
跨源资源共享(Cross-Origin Resource Sharing,CORS)是一种允许在受控的条件下,不同源的网页能够请求和共享资源的机制。
CORS 整个通信过程都是浏览器自动完成,浏览器一旦发现 ajax 请求跨源,就会自动在头信息中增加 Origin 字段,用来说明本次请求来自哪个源(协议+域名+端口)。因此,实现 CORS 通信的关键是服务器,需要服务器配置 Access-Control-Allow-Origin 头信息。
基本思想:
CORS 的请求根据是否会触发 OPTIONS 请求,分为两类:
需要满足的条件:
第一个类型为 preflight 就是预检请求:
查看预检请求的请求头数据:
OPTIONS 请求头中的特殊字段:
OPTIONS 响应头中的特殊字段:
OPTIONS 请求的更多信息,可参考官方文档
优点:
缺点:
后端进行代理中转请求至服务器端,然后将获取的数据返回给前端。
外网前端页面无法访问内网接口,配置代理接口允许前端页面访问,并中转内网接口,则外网前端页面可以跨域访问内网接口。
比如配置 Nginx,将接收到的请求转发到内网:
server{
#如果是静态文件,直接指向目录
location / {
root html;
index index.html index.htm;
}
# 如果是动态应用,用proxy_pass转发一下
location ~ ^/api/(.*?)$ {
proxy_pass http://127.0.0.1:8080/api/$1?$args;
}
}
优点:
缺点:
前端跨域通信是指浏览器中两个不符合同源策略的前端页面进行通信。
仅适用于主域相同,子域不同的前端通信跨域场景。
核心点:
页面 A:
<iframe id="iframe" src="http://b.qq.com/b.html">iframe>
<script>
document.domain = 'qq.com';
var windowB = document.getElementById('iframe').contentWindow;
alert('B页面的user变量:' + windowB.user);
script>
页面 B:
<script>
document.domain = 'qq.com';
var user = 'saramliu';
script>
利用 url 的 hash 值改变但不刷新页面的特性,实现简单的前端跨域通信。
受到浏览器安全机制的限制,A 嵌套 B,A 可以修改 B 的 hash 值,但 B 不能修改 A 的 Hash 值,需要一个与 A 同源的页面来中转。
A 页面:
<iframe id="iframe" src="http://b.qq1.com/b.html">iframe>
<script>
// 监听c.html传来的hash值
window.onhashchange = function () {
alert('B页面传递数据:' + location.hash.substring(1));
};
script>
B 页面:
<iframe id="iframe" src="http://a.qq.com/c.html">iframe>
<script>
// 向c.html传递hash值
var iframe = document.getElementById('iframe');
setTimeout(function () {
iframe.src = iframe.src + '#user=saramliu';
}, 1000);
script>
中转页面:
<script>
// 监听b.html传来的hash值
window.onhashchange = function () {
// 操作同域a.html的hash值,传递数据
window.parent.parent.location.hash = window.location.hash.substring(1);
};
script>
优点:
缺点:
window.name 属性在不同页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
A 嵌套 B,B 将要传递的数据附加到 window.name 上,然后跳转到与 A 同域名的 C,A 和 C 满足同源策略,A 可与获取到 C 的 window.name。
A
<iframe id="iframe" src="http://b.qq1.com/b.html">iframe>
<script>
var state = 0;
var iframe = document.getElementById('iframe');
iframe.onload = function () {
if (state === 1) {
// 第2次onload成功后,读取同域window.name中数据
alert(iframe.contentWindow.name);
} else if (state === 0) {
// 第1次onload成功后
state = 1;
}
};
script>
B:
<script>
window.name = '这里是B页面!';
window.location = 'http://a.qq.com/c.html'; // 跳转到与A同源的C
script>
优点:
缺点:
postMessage 是 HTML5 XMLHttpRequest Level2 中的 API,是一种安全的跨域通信方法,且是为数不多可以跨域操作的 window 属性之一,它通常用于解决以下方面的问题:
A 获得 B 的 window 对象后,A 调用 postMessage 方法发送一个个 MessageEvent 消息。B 通过监听 message 事件即可获取 A 传递的数据。
A:
<iframe id="iframe" src="http://b.qq1.com/b.html">iframe>
<script>
var iframe = document.getElementById('iframe');
iframe.onload = function () {
var data = { meesage: '这里是A页面发的消息' };
var url = 'http://b.qq1.com/b.html'; // 向B页面发送消息
iframe.contentWindow.postMessage(JSON.stringify(data), url);
};
window.addEventListener('message', function (e) {
alert('B页面发来消息:' + JSON.parse(e.data));
});
script>
B:
<script>
window.addEventListener(
'message',
function (e) {
alert('A页面发来消息:' + JSON.parse(e.data));
var data = { meesage: '这里是B页面发的消息' };
var url = 'http://a.qq.com/a.html';
window.parent.postMessage(JSON.stringify(data), url);
},
false
);
script>
优点:
缺点: