JavaScript之跨域

同源策略(Same origin Policy)

是什么?

浏览器出于安全方面的考虑,只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。

同源是什么?

如果协议,端口(如果指定了一个)和域名对于两个页面是相同的,则两个页面具有相同的源。

image.png

需要注意的是: 对于当前页面来说页面存放的 JS 文件的域不重要,重要的是加载该 JS 页面所在什么域

报错范例

当我们手写一个服务器,并启动,代码、显示结果如下。。。
server.js

var http = require('http')
var fs = require('fs')
var path = require('path')
var url = require('url')

http.createServer(function(req, res){

  var pathObj = url.parse(req.url, true)

  switch (pathObj.pathname) {
    case '/getWeather':
      console.log("get weather");
      res.end(JSON.stringify({beijing: 'sunny'}))
      break;

    default:
      fs.readFile(path.join(__dirname, pathObj.pathname), function(e, data){
        if(e){
          res.writeHead(404, 'not found')
          res.end('

404 Not Found

') }else{ res.end(data) } }) } }).listen(8011)

index.html




    
    Title


饥人谷

image.png

当我们修改index.html 中

     xhr.open('GET','http://localhost:8011/getWeather', true)
#   xhr.open('GET','http://127.0.0.1:8011/getWeather', true)
image.png

因为不是同源,所以会报错。
那我们的请求有没有发出去?答案是有的,看我们上面的代码,我们写了console.log("get weather")说明请求发出去了,所以终端会打印get weather


image.png

那我们请求发出去,有没有响应呢?别急继续往下看

image.png

也响应了,但是浏览器不接收,因为浏览器认为这个是不安全的,所以同源策略是浏览器的安全机制。
总之,同源策略就是当页面向接口发请求的时候,页面的URL和接口的URL是同源的。


JSONP

如果我们的页面向接口请求的话,假设是不同源的,我们该怎么做?
举个例子,我们手下有两个网站,A网站和B网站,A网站想使用B网站的某个接口获取数据,即使这两个网站在同一个服务器上,浏览器也是会阻止掉的,所以我们引用了JSONP。

是什么?

JSONP是通过 script 标签加载数据的方式去获取数据当做 JS 代码来执行 提前在页面上声明一个函数,函数名通过接口传参的方式传给后台,后台解析到函数名后在原始数据上「包裹」这个函数名,发送给前端。换句话说,JSONP 需要对应接口的后端的配合才能实现。

JSONP举例

server.js


var http = require('http')
var fs = require('fs')
var path = require('path')
var url = require('url')

http.createServer(function(req, res){
  var pathObj = url.parse(req.url, true)

  switch (pathObj.pathname) {
    case '/getNews':
      var news = [
        "第11日前瞻:中国冲击4金 博尔特再战200米羽球",
        "正直播柴飚/洪炜出战 男双力争会师决赛",
        "女排将死磕巴西!郎平安排男陪练模仿对方核心"
      ]
      /*防止中文乱码*/
      res.setHeader('Content-Type','text/json; charset=utf-8')
      /*下面是后端为jsonp实现要做的事情*/
      if(pathObj.query.callback){
        res.end(pathObj.query.callback + '(' + JSON.stringify(news) + ')')
      }else{
        res.end(JSON.stringify(news))
      }

      break;

    default:
      fs.readFile(path.join(__dirname, pathObj.pathname), function(e, data){
        if(e){
          res.writeHead(404, 'not found')
          res.end('

404 Not Found

') }else{ res.end(data) } }) } }).listen(8088)

index,html




注意上面的代码index.html的script.src要和server,js监听端口号一致,否则会出现下面问题

image.png

正常应该是这样显示

image.png

注意当前页面是localhost:8088/index.html,而我们请求的是127.0.0.1:8088/index.html
JSONP是比较HACK的跨域方法,现在不常使用


CORS(Cross-Origin Resource Sharing)

是什么?

CORS 全称是跨域资源共享(Cross-Origin Resource Sharing),是一种 ajax 跨域请求资源的方式,支持现代浏览器,IE支持10以上。 实现方式很简单,当你使用 XMLHttpRequest 发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin; 浏览器判断该相应头中是否包含 Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。所以 CORS 的表象是让你觉得它与同源的 ajax 请求没啥区别,代码完全一样。

CORS举例

server.js


var http = require('http')
var fs = require('fs')
var path = require('path')
var url = require('url')

http.createServer(function(req, res){
 var pathObj = url.parse(req.url, true)

 switch (pathObj.pathname) {
   case '/getNews':
     var news = [
       "第11日前瞻:中国冲击4金 博尔特再战200米羽球",
       "正直播柴飚/洪炜出战 男双力争会师决赛",
       "女排将死磕巴西!郎平安排男陪练模仿对方核心"
       ]

     res.setHeader('Access-Control-Allow-Origin','http://localhost:8081')
     //res.setHeader('Access-Control-Allow-Origin','*')
     res.end(JSON.stringify(news))
     break;
   default:
     fs.readFile(path.join(__dirname, pathObj.pathname), function(e, data){
       if(e){
         res.writeHead(404, 'not found')
         res.end('

404 Not Found

') }else{ res.end(data) } }) } }).listen(8081)

index.html




  

第一种是正常情况


image.png

第二种就是跨域


image.png

加了一个声明,Access-Control-Allow-Origin,允许哪些域使用我的数据。

JSONP和CORS本质是从不同域的接口获取数据,下面我们要说一下如何iframe跨域。

降域

比如当前页面下,我们埋了一个iframe,iframe对应的地址是taobao.com,假设你刚刚用电脑访问过淘宝,买了一些东西,然后又访问这个页面,而这个iframe会自动加载,并且是登录状态。假设浏览器没有做一些安全限制,那在当前页面下,我们是可以使用JavaScript来操控iframe上的内容,我们就可以知道淘宝用户是谁,当前金额是多少,我们甚至可以操作模拟用户的点击,所以浏览器不会让你这么做,所以浏览器想到一个办法,只有是当前域名的iframe,你才可以访问里面的东西。换句话说,我们的页面埋下的iframe页面,如果是不同域,虽然可以加载,但是外部的JavaScript是无法操作里的内容。

举例

a.html











使用降域实现跨域

b.html




  







image.png

上图分为左右框,右框为iframe页面,我们这时操作iframe,出现报错
因为当前页面和ifame页面是不同域的。
当我们修改使其为同一域

image.png

假使当前页面a.jrg.com ,而iframe为b.jrg.com ,这时我们使用document.domain='jrg.com' 这样就可以操作iframe了,这就是通过降域实现操作iframe


postMessage

假设现在我们有一个页面,我写的页面是在我的域名下。我希望我这个页面可以供其他人使用。其他人可以把我的页面作为iframe页面嵌入到自己的页面当中去。我还希望他们可以在他们的页面上操作我的页面,因为我愿意提供自己的页面,可是按照同源机制,只要域名不一样,他们就不可以操作我的页面,除非我们的主域是相同的,这时该怎么办呢?需要用到postmessage

是什么?

postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
postMessage(data,origin)方法接受两个参数
1.data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。
2.origin:字符串参数,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

比如说我们的网站嵌入一个官网叫mychat,我希望我的页面和它里面的内容进行交互,mychat就可以支持postmessage,我就可以向他post我的消息,它收到之后,做对应的事情,它给我一个文档告诉我怎么做。

举例

a.html













使用postMessage实现跨域

b.html




    
    


    
       





参考资料:同源策略
postmessage

你可能感兴趣的:(JavaScript之跨域)