跨源限制
在浏览器里
对源(url协议,主机名,端口号)不同的通信进行限制,在web领域 为了确保安全,只有同源的通信才能被允许进行,称为同源策略
虽然可以在html里使用iframe以实现在一个页面中同时显示来自不同域的文档,不过js仍然只能访问同一源的文档
如果文档的url和iframe的不同,则无法通过文档中包含的js对iframe内的dom进行操作,而iframe内的js也无法操作文档里的dom
如果不这样 ,就会发生诸如不同域的cookie能够互相访问等安全问题
对于XMLHttpRequest来说,同源策略的含义是,一个XMLHttpRequest对象只能发送至一个特定的服务器,即提供了使用该XMLHttpRequest对象的文档下载服务器
跨源通信
js里的跨源通信
JSONP
iframe***
window.postMessage()
XMLHttpRequest level 2
1.JSONP
利用script标签的src将其它域的js文件载入,如果在此处动态创建script标签的话,就能实现对其它域中数据的动态读取
不过仅仅是取得了数据,还无法在客户端使用。因此产生jsonp(json with padding ,padding指向json数据中添加函数名)
服务端会对数据添加函数名后返回
代码示例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script> </head> <body> <script> var url="http://b.cn/do.php?callback=foo"; //注意 foo函数的定义要先于 引入 url里的内容 function foo(res){ console.log(res); console.log(res.a); } function loadData(url){ var elem=document.createElement('script'); elem.src=url; document.getElementsByTagName('head')[0].appendChild(elem); } loadData(url); </script> </body> </html>
b.cn/do.php
<?php $arr=['a'=>'ajax','b'=>'bbc']; $callback=$_GET['callback']; $json=$callback."(".json_encode($arr).");"; echo $json; //foo({"a":"ajax","b":"bbc"}); ?>
请求的内容是执行代码的内容,在客户端是定义声明部分
jsonp的缺点
无法在post请求类型中使用
只能做到动态创建script标签并读取数据而无法从客户端发出数据
2. XMLHttpRequest level 2
XMLHttpRequest无法跨源通信
在XMLHttpRequest level2中 只要服务器端许可就可以实现
a.cn/a.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> <script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script> </head> <body> <div class="btn" style="cursor: pointer;text-align:center;width: 100px;height: 50px;line-height:50px;color:white;background: green">click</div> <script> $(function(){ $(".btn").click(function(){ var xhr=new XMLHttpRequest(); xhr.onreadystatechange=function(){ var state=xhr.readyState; if(state==4){ console.log(xhr.getAllResponseHeaders()); } } xhr.open('GET','http://b.cn/do.php?a=1&b=2'); xhr.withCredentials=true; //默认不发送cookie的,当设个参数设置为true服务端 Access-Control-Allow-Origin 必须明确指定 不可为* 而且必须设置 header('Access-Control-Allow-Credentials:true'); xhr.setRequestHeader("content-type",'multipart/form-data'); // xhr.setRequestHeader("content-type",'application/x-www-form-urlencoded'); xhr.send('p1=1'); }); }); </script> </body> </html>
http://b.cn/do.php
代码
<?php header('Access-Control-Allow-Origin:http://a.cn'); header('Access-Control-Allow-Credentials:true'); header('Access-Control-Allow-Methods:POST'); var_dump($_POST); var_dump($_GET); var_dump(file_get_contents("php://input")); var_dump($_COOKIE);
关于跨源发送cookie
xhr.withCredentials=true;
这里跨源指的是 跨的二级的域,根域必须相同,而且cookie的域必须是根域
比如 a.a.cn可以通过这里所说的跨源 在向b.a.cn发送ajax请求时 带上 .a.cn下的cookie
不要以为 域在.a.cn下的cookie ,a.a.cn在向b.a.cn发请求时 会自动带上
也不要认为cookie的跨源能跨根域,是不可能的
3.postMessage()
window.postMessage
是一个安全的跨源通信的方法。一般情况下,当且仅当执行脚本的页面使用相同的协议(通常都是 http)、相同的端口(http默认使用80端口)和相同的 host(两个页面的document.domain 的值相同)时,才允许不同页面上的脚本互相访问。 window.postMessage
提供了一个可控的机制来安全地绕过这一限制,当其在正确使用的情况下。
调用 window.postMessage时,
将分发一个
MessageEvent
事件到目标窗口, 在所有挂起必须执行的脚本完成后. (例如:当一个事件处理程序调用window.postMessage时,仍剩余事件处理程序, 先前的挂起等待超时等)。MessageEvent 有消息类型,它被设置为第一个参数值提供给window.postMessage的data属性,
对应的window调用window.postMessage的时候,window.postMessage主文档的来源的origin属性被称为源属性,指哪个调用window.postMessage的窗口。 (事件的其他标准属性都存在与对应的预期值。)
语法
otherWindow.postMessage(message, targetOrigin);
otherWindow
- 其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames
message
- 将要发送到其他 window的数据,将会被结构化克隆算法序列化。这意味着你可不受什么限制的安全传送数据对象给目标窗口而无需自己序列化
targetOrigin
-
通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;例如,当用
postMessage传送密码时,这个参数就显得尤为重要,必须保证它的值与这条包含密码的信息的预期接受者的orign属性完全一致,来防止密码被恶意的第三方截获。如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。
a.cn/a.html
<!DOCTYPE html> <html> <head> <title>Post Message</title> </head> <body> <div style="width:200px; float:left; margin-right:200px;border:solid 1px #333;"> <div id="color">Frame Color</div> </div> <div> <iframe id="child" src="http://b.cn/b.html"></iframe> </div> <script type="text/javascript"> window.onload=function(){ window.frames[0].postMessage('getcolor','http://b.cn'); } window.addEventListener('message',function(e){ console.log('a:',e); var color=e.data; document.getElementById('color').style.backgroundColor=color; },false); </script> </body> </html>
b.cn/b.html
<!doctype html> <html> <head> <style type="text/css"> html,body{ height:100%; margin:0px; } </style> </head> <body style="height:100%;"> <div id="container" onclick="changeColor();" style="widht:100%; height:100%; background-color:rgb(204, 102, 0);"> click to change color </div> <script type="text/javascript"> var container=document.getElementById('container'); window.addEventListener('message',function(e){ if(e.source!=window.parent) return; var color=container.style.backgroundColor; window.parent.postMessage(color,'*'); },false); function changeColor () { var color=container.style.backgroundColor; if(color=='rgb(204, 102, 0)'){ color='rgb(204, 204, 0)'; }else{ color='rgb(204,102,0)'; } container.style.backgroundColor=color; window.parent.postMessage(color,'*'); } </script> </body> </html>