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

什么是同源策略

同源策略 (Same-Origin Policy) 最早由 Netscape 公司提出, 所谓同源就是要求, 域名, 协议, 端口相同。非同源的脚本不能访问或者操作其他域的页面对象(如DOM等)。作为著名的安全策略, 虽然它只是一个规范, 并不强制要求, 但现在所有支持 javaScript 的浏览器都会使用这个策略。 以至于该策略成为浏览器最核心最基本的安全功能, 如果缺少了同源策略, web的安全将无从谈起。

同源策略要求三同, 即: 同域, 同协议, 同端口。

同域即host相同, 顶级域名, 一级域名, 二级域名, 三级域名等必须相同, 且域名不能与 ip 对应;
同协议要求, http与https协议必须保持一致;
同端口要求, 端口号必须相同。

什么是跨域?跨域有几种实现形式

只要协议、域名、端口有任何一个不同,都被当作是不同的域,跨域就是访问非本域的资源。

跨域实现方式
1、JSONP
2、CORS
3、降域
4、PostMessage

JSONP 的原理是什么

JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。它的基本思想是,AJAX 无法跨域是受到“同源政策”的限制,但是带有src属性的标签(例如<script>、<img>、<iframe>)是不受该政策限制的,因此通过向页面中动态添加标签来完成对跨域资源的访问,这也是 JSONP 方案最核心的原理。

通常我们使用都是引用的静态资源(主要是 js 文件),其实它也可以用来引用动态资源(php、jsp、aspx等),后台服务被访问后返回一个“JavaScript函数调用”形式的字符串,由于是字符串,因此在后台的时候不会起到任何作用,但到了前台,放入标签之内,就成了一个合法的 JavaScript 函数调用,实参是我们真正需要的数据,被调用的回调函数也已经实现了,因此就会顺利的被调用。

JSONP 最大的优点就是兼容性非常好,其原理决定了即便在非常古老的浏览器上也能够很好的被实现。

JSONP 的主要缺点有两个,一是只能 GET 不能 POST,因为是通过引用的资源,参数全都显式的放在URL里,和 AJAX 没有关系。二是存在安全隐患,动态插入标签其实就是一种脚本注入,会受到跨站脚本攻击。

CORS是什么

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

简单请求:浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。

非简单请求:是对服务器有特殊要求的请求,比如请求方法是PUTDELETE,或者Content-Type字段的类型是application/json

跨域的解决方式

JSONP

JSONP是服务器与客户端跨源通信的常用方法。利用script标签可以引入其它域的JS这一特性,来实现跨域访问接口。JSONP最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。但前提是后端支持。

1、动态添加<script>元素,向服务器发出请求

var change = document.querySelector('.change')
var newsList = document.querySelector('.news-list')

change.addEventListener('click', function(){
   var script = document.createElement('script');
   script.src= 'http://localhost:8080/getNews?callback=appendHtml'
   document.head.appendChild(script);
   document.head.removeChild(script);
})

2、用src 中 callback参数 指定回调函数的名字

function appendHtml(news){
  var html = '';
  for( var i=0; i<news.length; i++){
  html += '<li>' + news[i] + '</li>';
  }
  newsList.innerHTML = html;
  }
})

3、服务器收到这个请求以后,会将数据会放在回调函数的参数位置返回。

app.get('/getNews', function(req, res){
    var news = [
        "第11日前瞻:中国冲击4金 博尔特再战200米羽球",
        "正直播柴飚/洪炜出战 男双力争会师决赛",
        "女排将死磕巴西!郎平安排男陪练模仿对方核心",
        "没有中国选手和巨星的110米栏 我们还看吗?",
        "中英上演奥运金牌大战",
        "博彩赔率挺中国夺回第二纽约时报:中国因对手服禁药而丢失的奖牌最多",
        "最“出柜”奥运?同性之爱闪耀里约",
        "下跪拜谢与洪荒之力一样 都是真情流露"
    ]
    var data = [];
    for(var i=0; i<3; i++){
        var index = parseInt(Math.random()*news.length);
        data.push(news[index]);
        news.splice(index, 1);
    }
    var callback = req.query.callback;
    if(callback){
        res.send(callback + '('+ JSON.stringify(data) + ')');
    }else{
        res.send(data);
    }
})

JSONP的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。

JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

CORS

CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。在服务器中设置响应头字段即可:

res.header("Access-Control-Allow-Origin","url")

CORS与JSONP的使用目的相同,但是比JSONP更强大。

JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。

postMessage

postMessage() 方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。第二个参数可以是*但如果你设置了一个URL但不相符,那么该事件不会被分发。

父页面发送消息:

window.frames[0].postMessage('message', origin)

iframe接受消息:

window.addEventListener('message',function(e){
    if(e.source!=window.parent) return;//若消息源不是父页面则退出
      //TODO ...
});

其中 e 对象有三个重要的属性

  • data, 表示父页面传递过来的message
  • source, 表示发送消息的窗口对象
  • origin, 表示发送消息窗口的源(协议+主机+端口号)

推荐阅读更多精彩内容