浏览器出于安全方面的考虑,不一样源的客户端脚本在没有明确受权的状况下,不能读写对方的资源。javascript
同源指的是:html
同协议java
同域名ajax
同端口后端
做用:保证用户信息的安全,防止恶意的网站窃取数据api
例1:A网站是一家银行,用户登陆之后,又去浏览其余网站。若是其余网站能够读取A网站的 Cookie,会发生什么?很显然,若是 Cookie 包含隐私(好比存款总额),这些信息就会泄漏。除此以外,Cookie 每每用来保存用户的登陆状态,若是用户没有退出登陆,其余网站就能够冒充用户,随心所欲。(由于浏览器同时还规定,提交表单不受同源政策的限制)跨域
例2:恶意网站的页面经过iframe嵌入了银行的登陆页面(两者不一样源),若是没有同源限制,恶意网页上的javascript脚本就能够在用户登陆银行的时候获取用户名和密码,从而形成相关的风险。数组
对于当前页面来讲页面中 JS 文件的域不重要,重要的是当前页面所在的域与 脚本中涉及到的域(例如xht的open方法的url)是否同源浏览器
跨域:浏览器出于安全方面的考虑设置了同源策略来限制不一样域之间的交互,可是也阻碍了不域之间的协助。为了实现不一样域之间的交互、协做,所以须要“跨域”。安全
降域获取同一 Cookie:Cookie 是服务器写入浏览器的一小段信息,只有同源的网页才能共享。可是,两个网页一级域名相同,只是二级域名不一样,浏览器容许经过设置document.domain 共享 Cookie。
example:A 网页是 http://w1.example.com/a.html
,B 网页是http://w2.example.com/b.html
,那么只要设置相同的 document.domain
,两个网页就能够共享 Cookie
。
JavaScript
// A网页和B网页设置相同的 document.domain document.domain = 'example.com'
// A网页经过脚本设置 Cookie document.cookie = "test1 = hello";
// B网页能够获取到该 Cookie var otherCookie = document.cookie;
降域使不一样源的iframe窗口和父窗口相互通讯:若是两个网页不一样源,就没法拿到对方的
DOM。典型的例子是 iframe 窗口和与父窗口没法通讯。若是两个窗口一级域名相同,只是二级域名不一样,那么设置 document.domain 属性,就能够规避同源政策,拿到
DOM。
A 网页:URL: http://a.yanxin.com:8080/a.html
HTML
<div class="ct"> <h1>使用降域实现跨域</h1> <div class="main"> <input type="text" placeholder="http://a.yanxin.cn:8080/a.html"> </div> <iframe src="http://b.yanxin.com:8080/b.html" frameborder="0" ></iframe> </div>
script
<script> document.querySelector('.main input').addEventListener('input', function(){ console.log(this.value); /* window.frames 是窗口中全部命名的框架组成的数组。 这个数组的每一个元素都是一个Window对象,对应于窗口中的一个框架。 window.frames[0]获得的就是html中的框架 window.frames[0].document.querySelector('input') 获得框架中的input元素 */ window.frames[0].document.querySelector('input').value = this.value; }) document.domain = "yanxin.com" </script>
iframe 中的 B 网页:URL: http://b.yanxin.com:8080/b.html
HTML
<input id="input" type="text" placeholder="http://b.yanxin.com:8080/b.html">
script
<script> document.querySelector('#input').addEventListener('input', function(){ // 获得父窗口中的input元素 window.parent.document.querySelector('input').value = this.value; }) document.domain = 'yanxin.com'; </script>
HTML5为了解决跨域问题,引入了一个全新的API:跨文档通讯 API(Cross-document messaging)。
这个API为window对象新增了一个window.postMessage方法,容许跨窗口通讯,不论这两个窗口是否同源。
目的:向另外一个地方传递数据,另外一个地方指的是:包含在当前页面的<iframe>元素,或者由当前页面弹出的窗口
window.postMessage() 方法被调用时,会在全部页面脚本执行完毕以后向目标窗口派发一个 MessageEvent 消息。 * otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow:其余窗口的一个引用(相对于当前的窗口的其余窗口),好比iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。
message将要发送到其余 window的数据
targetOrigin:指定哪些窗口能接收到消息事件,其值能够是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,若是目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者彻底匹配,消息才会被发送。
注意:若是你明确的知道消息应该发送到哪一个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。不提供确切的目标将致使数据泄露到任何对数据感兴趣的恶意站点。
示例
页面 A: http://a.yanxin.cn:8080/a.html
。
在页面 A 中 打开页面 B: http://b.yanxin.cn:8080/b.html
当点击页面 A 上的 button 时,向页面B传输消息 "hello world"
HTML
<button id="sendmessage">send message</button>
script
<script> var button = document.querySelector("#sendmessage"); // 打开页面b,而且取得对它的引用 var targetWindow = window.open("http://b.yanxin.cn:8080/b.html"); button.addEventListener('click',function(){ // 注意: 这里是向targetWindow即新打开的窗口(经过上面的window.open打开的窗口)发送消息 // 直接打开页面b(手动打开的)是没法收到消息的 // 若是直接调用postMessage则至关于当前窗口向本身发送消息 targetWindow.postMessage("hello world!!", "http://b.yanxin.cn:8080/b.html"); }); </script>
为页面 B 的 window 添加监听器
当页面 B 收到 Message 时,在控制台中输出收到的消息,并提示 "hello"
<script> window.addEventListener("message", receiveMessage); function receiveMessage(event){ console.log(event.data); alert("hello"); } </script>
CORS: Cross-Origin Resource Sharing, 跨源资源共享,是W3C的一个工做草案,定义了在必须访问跨源资源时,浏览器与服务器应该如何沟通,它是一种 ajax跨域请求资源的方式,支持现代浏览器,IE支持10以上。
实现过程:当使用 XMLHttpRequest发送请求时,浏览器发现该请求不符合同源策略,会自动给该请求加一个请求头:Origin,并将请求发送。服务器端收到请求后,若是肯定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin; 浏览器收到响应后判断该相应头中是否包含 Origin 的值,若是有则浏览器会处理响应,咱们就能够拿到响应数据,若是不包含浏览器直接驳回,这时咱们没法拿到响应数据。整个CORS通讯过程,都是浏览器自动完成,不须要用户参与
实现CORS通讯的关键是服务器。只要服务器实现了CORS接口,就能够跨源通讯。
详细流程:
1.浏览器发现此次请求不符合同源策略,就自动在头信息之中,添加一个Origin字段。
GET /cors HTTP/1.1 Origin: http://api.bob.com Host: api.alice.com Accept-Language: en-US Connection: keep-alive User-Agent: Mozilla/5.0...
在上面的头信息中,Origin字段代表了本次请求来自哪一个源:协议、域名、端口。
2.该请求到达服务器后,服务器会根据这个值来判断是否接受请求。若是Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段(以下所示)。若是Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。
Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: FooBar Content-Type: text/html; charset=utf-8
3.浏览器收到响应后判断该相应头中是否包含Origin的值,若是响应的头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获,这时咱们没法拿到响应数据。若是有则浏览器会处理响应,咱们就能够拿到响应数据。
基本思想是,网页经过添加一个<script>元素,向服务器请求JSON数据,这种作法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。传回后回调函数当即执行(参数是后端产生的数据),从而实现相应的功能。
具体流程:
1.网页动态地插入 <script> 元素,由它向跨域网址发出请求
JavaScript
function addScriptTag(src){ var script = document.createElement('script'); script.src = src; //跨域网址 document.body.appendChild(script); // 往页面插入元素后,会向跨域网址发出请求(src指定了跨域网址,获得响应后当即执行 } window.onload = function(){ addScriptTag("http://example.com/ip?callback = foo'); //当页面加载完毕,即往页面中插入script元素 } function foo(data){ console.log('your public ip address is: '+ data.ip)' }
2.上面代码经过动态添加<script>元素,向服务器example.com发出请求。注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字,这对于JSONP是必需的。
3.服务器收到该请求后,会将数据放在回到函数的参数位置返回
JavaScript
foo({ "ip": "8.8.8.8" });
4.因为<script>元素请求的脚本,直接做为代码运行。这时,只要浏览器定义了foo函数,该函数就会当即调用。做为参数的JSON数据被视为JavaScript对象,而不是字符串,所以避免了使用JSON.parse的步骤。
注意:JSONP只能发 GET 请求(由于请求是放在<script>的scr中的)。
咱们知道,一个网页能够从任何网页中加载图像,不用担忧跨域问题。这也是在线广告跟踪浏览量的主要方式。咱们能够动态地建立图像,使用它们的onload和onerror事件处理程序来肯定是否接受到了响应。
动态建立图像常常用于图像Ping,图像Ping是与服务器进行简单、单向的跨域通讯的一种方式。请求的数据是经过查询字符串形式发送的,而响应能够是任意内容,但一般是像素图或者204响应。经过图像Ping,浏览器得不到任何具体的数据,但经过侦听load和error事件,它能知道响应是何时接受到的。
var img = new Image() img.onload = img.onerror = function(){ alert("Done") }; img.src = "http://www.example.com/test?name=Nicholas";
这里建立了一个 Image 的实例,而后将 onload 和 onerror 事件处理程序指定为同一个函数。这样不管是什么响应,只要请求完成,就能获得通知。请求从设置 src 属性那一刻开始,而这个例子在请求中发送了一个 name 参数。
图像 Ping 最经常使用于跟踪用户点击页面或动态广告曝光次数。
缺点:
只能发送GET请求
没法访问服务器的响应文本
结论:图像Ping只能用于浏览器与服务器间的单向通讯。