浏览器同源策略和跨域方法

https://www.jianshu.com/p/1d0ee9bac639

http://www.javashuo.com/article/p-mrypbjlr-z.html

http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

http://www.ruanyifeng.com/blog/2016/04/cors.html

浏览器同源策略及处理办法

1、什么是浏览器的同源策略

同源是指同协议同域名同端口javascript

 
同源策略.png

 

注:IE 未将端口号加入到同源策略的组成部分之中php

浏览器同源策略的目的是为了保证用户信息的安全,防止恶意的网站窃取数据。若是网页之间不知足同源要求,将不能:html

  1. 共享Cookie、LocalStorage、IndexDB
  2. 获取DOM
  3. AJAX请求不能发送

2、既然知道这一策略,开发中如何规避呢?

(1)经过设置window.domain的方式实现共享cookie

这种方法适用于一级域名相同,二级域名不一样的时候使用前端

例如:如今有www.example.comexample.com两个网页,能够经过设置window.domain的形式来处理,前端页面能够经过设置window.domain=example.com或者后台设置Set-Cookie: key=value; domain=.example.com; path=/来实现共享cookiejava

(2)经过修改片断标识符的方法实现跨域

这种方法适用于iframe嵌套网页间的跨域。片断标识符就是URL的#号后面的部分。改变片断标识符,页面不会刷新。web

父窗口向子窗口传递信息ajax

//父窗口 var src = originURL + '#' + data; document.getElementById('myIFrame').src = src; //子窗口经过监听hashchange事件回去父窗口传递的信息 window.onhashchange = checkMessage; function checkMessage() { var message = window.location.hash; // ... } 

子窗口向父窗口传递信息json

//子窗口修改父窗口的片断标识符,一样,父窗口也能够经过hashchange事件来获取数据 parent.location.href= target + "#" + hash; 

(3)使用window.name处理跨域问题

window.name属性具备以下特色,不管是否同源,只要在同一窗口中设置了该属性,后一个网页就能够读取到。该方法借助window.name这一属性和iframe具备跨域能力的关键点,来处理跨域问题。后端

首先,在localhost:8080服务器建立文件demo.html和空白文件proxy.html,而后,在localhost:8081服务器建立data.html文件,文件内容以下所示:跨域

//localhost:8080/demo.html
<!doctype html> <html> <head> <title>demo</title> </head> <body> <h1>demo</h1> <iframe id="myIframe" width="400" height="400"></iframe> <script> var myIframe = document.getElementById('myIframe'); myIframe.src = 'http://localhost:8081/yzdj-mobile-web/data.html'; var state = 0; myIframe.onload = function(){ if(state === 1) { console.log(myIframe.contentWindow.name); myIframe.contentWindow.document.write(''); myIframe.contentWindow.close(); }else if(state === 0){ state = 1; myIframe.contentWindow.location = 'http://localhost:8080/yzdj-admin-web/proxy.html'; } } </script> </body> </html> 
//localhost:8081/data.html
<!doctype html> <html> <head> <title>data</title> </head> <body> <h1>data</h1> <script type="text/javascript"> window.name = '{a:1,b:2}'; </script> </body> </html> 

这种解决方法的优势是window.name容量大,缺点是:须要监听window.name变化,影响网页性能。

(4)使用window.postMessage解决跨域

这种方法借助HTML5的跨文档API(cross-document messaging)中增长的window.postMessage方法,容许跨窗口通讯,无论窗口是否同源。父窗口和子窗口能够监听message事件,获取对方发送的消息。这种方法能够实现LocalStorage等信息的共享。

messageevent对象提供三个属性:

  • event.source:发送消息的窗口
  • event.origin: 消息发向的网址
  • event.data: 消息内容

下面分别使用window.open的方法和嵌套iframe的方法实现父子窗口之间的通讯
使用window.open方法父页面:

//localhost:8080
<!doctype html> <html> <head> <title>admin</title> </head> <body> <h1 class="header">parent</h1> <div class="mb20"> <textarea name="ta" id="data" cols="30" rows="5">hello world</textarea> <button style="font-size:20px;" onclick="send()">post message</button> </div> <script> var pop = window.open('http://localhost:8181/yzdj-mobile-web/demo.html'); function send() { var data = document.querySelector('#data').value; pop.postMessage(data, 'http://localhost:8181/'); // 触发跨域子页面的messag事件 } window.addEventListener('message', function(messageEvent) { var data = messageEvent.data; console.info('message from child:', data); }, false); </script> </body> </html> 

子页面:

//localhost:8081
<!doctype html> <html> <head> <title>mobile</title> </head> <body> <h1 class="header">chidren</h1> <input type="text" id="inp" value="some contents.."> <button onclick="send()">send</button> <script> var origin =''; var source = ''; window.addEventListener('message', function(ev) { var data = ev.data; origin = ev.origin; source = ev.source console.info('message from parent:', data); }, false); function send() { var data = document.querySelector('#inp').value; source.postMessage(data, origin); // 若父页面的域名和指定的不一致,则postMessage失败 } </script> </body> </html> 

使用iframe嵌套的方法,父页面

<!doctype html> <html> <head> <title>parent</title> </head> <body> <h1 class="header">parent</h1> <div class="mb20"> <textarea name="ta" id="data" cols="30" rows="5">hello world</textarea> <button style="font-size:20px;" onclick="send()">post message</button> </div> <!-- 跨域的状况 --> <iframe src="http://localhost:8081/b.html" id="child" style="display: block; border: 1px dashed #ccc; height: 300px;"></iframe> <script> function send() { var data = document.querySelector('#data').value; window.frames[0].postMessage(data, 'http://localhost:9022/'); // 触发跨域子页面的messag事件 } window.addEventListener('message', function(messageEvent) { var data = messageEvent.data; console.info('message from child:', data); }, false); </script> </body> </html> 

子页面

<!doctype html> <html> <head> <title>chilren</title> </head> <body> <h1 class="header">chilren</h1> <input type="text" id="inp" value="some contents.."> <button onclick="send()">send</button> <script> window.addEventListener('message', function(ev) { var data = ev.data; console.info('message from parent:', data); }, false); function send() { var data = document.querySelector('#inp').value; parent.postMessage(data, 'http://localhost:9011/'); // 若父页面的域名和指定的不一致,则postMessage失败 } </script> </body> </html> 

(5)使用JSONP来实现AJAX的跨域

定义和用法:经过动态插入一个script标签。浏览器对script的资源引用没有同源限制,同时资源加载到页面后会当即执行(没有阻塞的状况下)。

特色:经过状况下,经过动态建立script来读取他域的动态资源,获取的数据通常为json格式。

实例以下:

<script>

    function testjsonp(data) {

       console.log(data.name); // 获取返回的结果

    }

</script>

<script>

    var _script = document.createElement('script');

    _script.type = "text/javascript";

    _script.src = "http://localhost:8888/jsonp?callback=testjsonp";

    document.head.appendChild(_script);

</script>

动态建立一个 script 标签,而且告诉后端回调函数名叫 handleResponse
// 1. 定义一个 回调函数 handleResponse 用来接收返回的数据
function handleResponse(data) { console.log(data); };
// 2.var body = document.getElementsByTagName('body')[0];
var script = document.gerElement('script');
script.src = 'http://www.laixiangran.cn/json?callback=handleResponse';
body.appendChild(script);
// 3.经过 script.src 请求 `http://www.laixiangran.cn/json?callback=handleResponse`,
// 4. 后端可以识别这样的 URL 格式并处理该请求,而后返回 handleResponse({"name": "laixiangran"}) 给浏览器
// 5. 浏览器在接收到 handleResponse({"name": "laixiangran"}) 以后当即执行 ,也就是执行 handleResponse 方法,得到后端返回的数据,这样就完成一次跨域请求了。

或者使用jq的ajax方法

$.ajax({
    url:'http://www.example.com/getData', dataType:'jsonp', jsonp:'callback', data:{ wd:'XX.value' }, success:function(result){ alert(result.s); } , error:function(err){ alert(err) } }); 

这种方法须要和设置修改请求网址的后台,代码中的callback须要和后台设置的一致

优势

  • 使用简便,没有兼容性问题,目前最流行的一种跨域方法。

缺点

  • 只支持 GET 请求。
  • 因为是从其它域中加载代码执行,所以若是其余域不安全,极可能会在响应中夹带一些恶意代码。
  • 要肯定 JSONP 请求是否失败并不容易。虽然 HTML5 给 script 标签新增了一个 onerror 事件处理程序,可是存在兼容性问题。

(6)webSocket

是一种通讯协议,使用ws://wss://做为协议前缀,这种协议不受同源政策的影响,只要服务器支持就能够。

(7)CORS(Cross-Origin Resource Sharing,跨源资源分享)

它容许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。这种解决跨域方法的优势是容许任何形式的请求,而JSONP只能发GET请求。

实现CORS通讯的关键是服务器,只要服务器实现了CORS接口,就可跨源通讯

简单请求(simple request)和非简单请求(not-so-simple request)

(1) 请求方法是如下三种方法之一:
HEAD
GET
POST
(2)HTTP的头信息不超出如下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

不一样时知足上面条件的就是非简单请求。

对于简单请求,浏览器直接发出CORS请求。具体来讲,就是在头信息之中,增长一个Origin字段,用来讲明本次请求的是哪一个源。服务器根据这个字段来判断是否容许请求。

非简单请求是那种对服务器有特殊要求的请求,好比请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
对于非简单请求,会在正式通讯以前,增长一次HTTP查询请求,称为预检(preflight)请求。

服务器收到"预检"请求之后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段之后,确认容许跨源请求,就能够作出回应。一旦服务器经过了"预检"请求,之后每次浏览器正常的CORS请求,就都跟简单请求同样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

优势

  • CORS 通讯与同源的 AJAX 通讯没有差异,代码彻底同样,容易维护。
  • 支持全部类型的 HTTP 请求。

缺点

  • 存在兼容性问题,特别是 IE10 如下的浏览器。
  • 第一次发送非简单请求时会多一次请求。

(8)还有一种方法是使用Nginx转发。

相关文章
相关标签/搜索