一、何为同源
所谓”同源”指的是”三个相同”:
http://www.example.com/dir2/other.html:同源
http://example.com/dir/other.html:不同源(域名不同)
http://v2.www.example.com/dir/other.html:不同源(域名不同)
http://www.example.com:81/dir/other.html:不同源(端口不同)
二、限制范围
如果非同源,如下行为会受到限制:
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 无法获得
- AJAX 请求不能发送
跨域类型:这里暂且分为两种:主域相同,子域不同的跨域和完全跨域
三、主域相同,子域不同的跨域
1.共享Cookie:设置document.domain
方法a:
1 2 3 4 5 6 7
| document.domain = 'example.com'; document.cookie = "test1=hello";
document.domain = 'example.com'; var allCookie = document.cookie;
|
方法b:
服务器在设置Cookie的时候,指定Cookie的所属域名为一级域名,比如.example.com,这样的话,二级域名和三级域名不用做任何设置,都可以读取这个Cookie
1
| Set-Cookie: key=value; domain=.example.com; path=/
|
2.获取DOM、变量:设置document.domain + iframe
1 2 3 4 5 6
| <iframe id="iframe" src="http://child.domain.com/b.html"></iframe> <script> document.domain = 'domain.com'; var user = 'admin'; </script>
|
1 2 3 4 5 6
| <script> document.domain = 'domain.com'; alert('get js data from parent ---> ' + window.parent.user); </script>
|
四、完全跨域
1.通信:片段标识符 + iframe
片段标识符(fragment identifier)指的是,URL的#号后面的部分,比如http://example.com/x.html#fragment的#fragment。如果只是改变片段标识符,页面不会重新刷新
1 2 3
| var src = originURL + '#' + data; document.getElementById('myIFrame').src = src;
|
1 2 3 4 5 6 7
| window.onhashchange = checkMessage;
function checkMessage() { var message = window.location.hash; }
|
1 2
| parent.location.href= target + "#" + hash;
|
2.通信:window.postMessage
用法:postMessage(data,origin)方法接受两个参数
data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
origin: 协议+主机+端口号,也可以设置为”*”,表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为”/“。
message事件的事件对象event,提供以下三个属性:
- event.source:发送消息的窗口
- event.origin: 消息发向的网址
- event.data: 消息内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe> <script> var iframe = document.getElementById('iframe'); iframe.onload = function() { var data = { name: 'aym' }; iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com'); };
window.addEventListener('message', function(e) { if (e.origin !== 'http://www.domain2.com') return; alert('data from domain2 ---> ' + e.data); }, false); </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <script> window.addEventListener('message', function(e) { if (e.origin !== 'http://www.domain1.com') return; alert('data from domain1 ---> ' + e.data);
var data = JSON.parse(e.data); if (data) { data.number = 16;
window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com'); } }, false); </script>
|
3.LocalStorage: window.postMessage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| window.onmessage = function(e) { if (e.origin !== 'http://bbb.com') return; var payload = JSON.parse(e.data); switch (payload.method) { case 'set': localStorage.setItem(payload.key, JSON.stringify(payload.data)); break; case 'get': var parent = window.parent; var data = localStorage.getItem(payload.key); parent.postMessage(data, 'http://aaa.com'); break; case 'remove': localStorage.removeItem(payload.key); break; } };
|
1 2 3 4 5 6 7 8 9 10 11 12
| var win = document.getElementsByTagName('iframe')[0].contentWindow; var obj = { name: 'Jack' };
win.postMessage(JSON.stringify({key: 'storage', method: 'set', data: obj}), 'http://bbb.com');
win.postMessage(JSON.stringify({key: 'storage', method: "get"}), "*"); window.onmessage = function(e) { if (e.origin != 'http://aaa.com') return; console.log(JSON.parse(e.data).name); };
|
4.Ajax: 通过jsonp跨域
只能发GET请求,原理可参考之前的 这篇 ,原生js实现:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script> var script = document.createElement('script'); script.type = 'text/javascript';
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack'; document.head.appendChild(script);
function onBack(res) { alert(JSON.stringify(res)); } </script>
|
jQuery实现:
1 2 3 4 5 6 7
| $.ajax({ url: 'http://www.domain2.com:8080/login', type: 'get', dataType: 'jsonp', jsonpCallback: "onBack", data: {} });
|
5.Ajax: 跨域资源共享(CORS)
普通跨域请求:只要服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。
原生ajax实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open('post', 'http://www.domain2.com:8080/login', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send('user=admin');
xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { alert(xhr.responseText); } };
|
jQuery ajax实现:
1 2 3 4 5 6 7 8
| $.ajax({ ... xhrFields: { withCredentials: true }, crossDomain: true, ... });
|
参考链接:
浏览器同源政策及其规避方法 - 阮一峰
前端常见跨域解决方案(全)
10种跨域解决方案