前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布

跨域

作者头像
bamboo
发布2019-01-29 15:51:52
2.1K0
发布2019-01-29 15:51:52
举报

一、同源策略

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

1、本域

同协议:如都是http或者https 同域名:如都是http://jirengu.com/ahttp://jirengu.com/b 同端口:如都是80端口 举个例子

代码语言:javascript
复制
http://jirengu.com/a/b.js 和 http://jirengu.com/index.php (同源)
代码语言:javascript
复制
不同源
http://jirengu.com/main.js 和 https://jirengu.com/a.php (协议不同)
http://jirengu.com/main.js 和 http://bbs.jirengu.com/a.php (域名不同,域名必须完全相同才可以)
http://jiengu.com/main.js 和 http://jirengu.com:8080/a.php (端口不同,第一个是80)

2、通过ajax获取数据,演示同源和不同源

首先打开hosts文件(window的地址是C:WindowsSystem32driversetchosts ),添加两条host记录

clipboard.png
clipboard.png

新建一个index.html文件,里面实现一个ajax获取数据的功能

代码语言:javascript
复制
<h1>hello world</h1>
<script>
  var xhr = new XMLHttpRequest()
  xhr.open('GET','http://localhost:8080/getWeather', true)
  xhr.send()
  xhr.onload = function(){
    console.log(xhr.responseText)
  }
</script>

新建一个js文件,里面实现一个静态路由功能的服务器

代码语言:javascript
复制
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':
      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('<h1>404 Not Found</h1>')
        }else{
          res.end(data)
        }
      }) 
  }
}).listen(8080)

gitbash cd到当前文件夹,通过node server.js打开服务器。 在浏览器输入localhost:8080,结果成功获取ajax数据

clipboard.png
clipboard.png

我把ajax请求地址改成http://a.com:8080/getWeather,结果报错了

clipboard.png
clipboard.png

我把浏览器的地址改为a.com 把ajax的地址改为b.com或者localhost,或者127.0.0.1都会出现跨域报错,即使他们的地址都是指向服务器。

二、JSONP(JSON with padding)

1、概念

HTML 中 script 标签可以加载其他域下的js,也就是说script的src能使用任何网站对应得文件,只要该网站愿意去提供这个东西。

代码语言:javascript
复制
<script src="http://api.jirengu.com/weather.php"></script>

这时候会向天气接口发送请求获取数据,获取数据后做为 js 来执行。 但这里有个问题, 数据是 JSON 格式的数据,直接作为 JS 运行的话我如何去得到这个数据来操作呢?

这样我们可以和后端商量一下,这样执行:

代码语言:javascript
复制
<script>
function showData(ret){
console.log(ret);
}
</script>
<script src="http://api.jirengu.com/weather.php?callback=showData"></script>

前端提前定义好showdata这个函数。当这个请求到达后端后,后端会去解析callback这个参数获取到字符串showData,在发送数据做如下处理: 之前后端返回数据: {"city": "hangzhou", "weather": "晴天"} 现在后端返回数据: showData({"city": "hangzhou", "weather": "晴天"}) 前端script标签在加载数据后会把 「showData({“city”: “hangzhou”, “weather”: “晴天”})」做为 js 来执行。实际上就是调用showData这个函数,同时参数是 {“city”: “hangzhou”, “weather”: “晴天”}。

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

2、举个栗子

2.1代码

代码语言:javascript
复制
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')
      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('<h1>404 Not Found</h1>')
        }else{
          res.end(data)
        }
      }) 
  }
}).listen(8080)
代码语言:javascript
复制
<!DOCTYPE html>
<html>
<body>
  <div class="container">
    <ul class="news">
    </ul>
    <button class="show">show news</button>
  </div>

<script>

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

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

  function $(id){
    return document.querySelector(id);
  }
</script>

</html>

打开终端,cd到对应的文件夹,输入 node server.js ,浏览器打开 http://localhost:8080/index.html。

2.2执行结果

![clipboard.png](/img/bVbk

clipboard.png
clipboard.png

2.3分析

2.3.1

代码语言:javascript
复制
res.end(pathObj.query.callback + '(' + JSON.stringify(news) + ')')

数组和字符串相加,会把数组toString(),将数组转换为以‘,’分隔的字符串【1,2,3】=>'1,2,3'。如果我不用json.stringify传递的news就是一个字符串,html里面就无法当成数组使用

clipboard.png
clipboard.png

2.3.2

代码语言:javascript
复制
 if(pathObj.query.callback){
 res.end(pathObj.query.callback + '(' + JSON.stringify(news) + ')')

以下是pathobj的输出值,可以看到pathObj.query是一个对象,所以才可以使用pathObj.query.callback获取对应的值

clipboard.png
clipboard.png

2.3.3

代码语言:javascript
复制
 function appendHtml(news){
    console.log(news);  
    var html = '';
    for( var i=0; i<news.length; i++){
      html += '<li>' + news[i] + '</li>';
    }
    console.log(html);
    $('.news').innerHTML = html;
  }

appendHtml函数收到的参数news是一个数组,和html参数是一个字符串

clipboard.png
clipboard.png

2.3.4

代码语言:javascript
复制
 script.src = 'http://127.0.0.1:8080/getNews?callback=appendHtml';
    document.head.appendChild(script);
    document.head.removeChild(script);

把script放在document.head,是为了让这句代码执行发送请求。删除removechild,是为了美观,如果不删除,每次点击news都会重新产生一个script。如下图我点击news一次,head就会新增一个script

clipboard.png
clipboard.png

三、CORS

关于cors,我只是记录了一个简单的情况,经用于入门。详细可以看这篇文章跨域资源共享 CORS 详解

1、概念

CORS 全称是跨域资源共享(Cross-Origin Resource Sharing),是一种 ajax 跨域请求资源的方式,支持现代浏览器,IE支持10以上。

2、实现原理

当你使用 XMLHttpRequest 发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin。 后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin(允许访问控制的域)和对应的值; 浏览器判断该相应头中是否包含 Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。

3、代码

server.js

代码语言:javascript
复制
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:8080')
      //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('<h1>404 Not Found</h1>')
        }else{
          res.end(data)
        }
      }) 
  }
}).listen(8080)

index.html

代码语言:javascript
复制
<!DOCTYPE html>
<html>
<body>
  <div class="container">
    <ul class="news">

    </ul>
    <button class="show">show news</button>
  </div>

<script>

  $('.show').addEventListener('click', function(){
    var xhr = new XMLHttpRequest()
    xhr.open('GET', 'http://127.0.0.1:8080/getNews', true)
    xhr.send()
    xhr.onload = function(){
      appendHtml(JSON.parse(xhr.responseText))
    }
  })

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

  function $(selector){
    return document.querySelector(selector)
  }
</script>



</html>

4、执行结果

http://127.0.0.1:8080/index.html,不跨域的打开index.html发送请求时,请求头内部没有origin

clipboard.png
clipboard.png

当我用http://localhost:8080打开index.html,出现跨域时。 请求头内部有Origin: http://localhost:8080,响应头有Access-Control-Allow-Origin: http://localhost:8080。两者相等,正常的获取数据

clipboard.png
clipboard.png

当我使用了a.com打开index.html时(我修改了host文件让a.com也指向127的本机服务器地址),出现了报错。因为服务器不允许a.com的网页使用资源

clipboard.png
clipboard.png

5、代码解析

代码语言:javascript
复制
res.setHeader('Access-Control-Allow-Origin','http://localhost:8080')
//res.setHeader('Access-Control-Allow-Origin','*')

这个表示服务器添加允许控制的域对应的地址,*表示允许所有接口用服务器的资源

四、降域

1、iframe和网页不同源

网页的内联iframe和网页不同源,我们不能通过js操作该iframe.比如我们在自己的网站上嵌套一个淘宝的frame,等用户先登陆了淘宝,然后登陆我们的网页的时候,处于frame的淘宝也是登陆状态,如果我能用js去操作这个用户的淘宝,那我就可以做很多坏事了。 2、举个例子

代码语言:javascript
复制
<html>
<head>
  <meta charset="utf-8">

<style>
  .ct{
    width: 910px;
    margin: auto;
  }
  .main{
    float: left;
    width: 450px;
    height: 300px;
    border: 1px solid #ccc;
  }
  .main input{
    margin: 20px;
    width: 200px;
  }
  .iframe{
    float: right;
  }

  iframe{
    width: 450px;
    height: 300px;
    border: 1px dashed #ccc;
  }
</style>
</head>
<div class="ct">
  <h1>使用降域实现跨域</h1>
  <div class="main">
    <input type="text" placeholder="http://a.com:8080/a.html">
  </div>

  <iframe src="http://b.com:8080/b.html" frameborder="0" ></iframe>

</div>


<script>
//URL: http://a.jrg.com:8080/a.html

document.querySelector('.main input').addEventListener('input', function(){
  console.log(this.value);
  window.frames[0].document.querySelector('input').value = this.value;
})


document.domain = "jrg.com"



</script>
</html>

首先修改host文件,把a.com和b.com指向127.0.0.1,打开http-server。

clipboard.png
clipboard.png

用a.com网址打开a.html,其中b.jrg.com的iframe的地址是b.com,和网页不同源的。可以看到该frame可以正确加载,但我们不能用js操作它

clipboard.png
clipboard.png

用b.com地址打开a.html,其中b.jrg.com的iframe的地址是b.com,和网页同源。我们就可以用js去操作该iframe。

clipboard.png
clipboard.png

2、降域

如果当前页面和iframe域名后面部分一致都是jrg.com,我们可以使用document.domain = "jrg.com"降域的方式来实现跨域

clipboard.png
clipboard.png

五、postMessage

通过postMessage实现不同主域下frame的操作

代码语言:javascript
复制
window.frames[0].postMessage(this.value,'*');//* ,表示任何域下都可接受请求
代码语言:javascript
复制
 window.parent.postMessage(this.value, '*');
代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <html>
<style>
    html,body{
        margin: 0;
    }
    input{
        margin: 20px;
        width: 200px;
    }
</style>

    <input id="input" type="text"  placeholder="http://b.jrg.com:8080/b.html">
<script>

// URL: http://b.jrg.com:8080/b.html
 
$('#input').addEventListener('input', function(){
    window.parent.postMessage(this.value, '*');
    
})

window.addEventListener('message',function(e) {
        $('#input').value = e.data
        console.log(e.data);
});

function $(id){
    return document.querySelector(id);
}    

</script>

</body>
</html>
代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

<style>
    .ct{
        width: 910px;
        margin: auto;
    }
    .main{
        float: left;
        width: 450px;
        height: 300px;
        border: 1px solid #ccc;
    }
    .main input{
        margin: 20px;
        width: 200px;
    }
    .iframe{
        float: right;
    }

    iframe{
        width: 450px;
        height: 300px;
        border: 1px dashed #ccc;
    }
</style>

<div class="ct">
    <h1>使用postMessage实现跨域</h1>
    <div class="main">
        <input type="text" placeholder="http://a.jrg.com:8080/a.html">
    </div>

    <iframe src="http://localhost:8080/b.html" frameborder="0" ></iframe>

</div>


<script>
//URL: http://a.jrg.com:8080/a.html

$('.main input').addEventListener('input', function(){
    console.log(this.value);
    window.frames[0].postMessage(this.value,'*');//* ,表示任何域下都可接受请求
})

window.addEventListener('message',function(e) {
        $('.main input').value = e.data
    console.log(e.data);
});



function $(id){
    return document.querySelector(id);
}

</script>
    
</body>
</html>

打开http-server,查看结果

clipboard.png
clipboard.png
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-12-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、同源策略
    • 1、本域
      • 2、通过ajax获取数据,演示同源和不同源
      • 二、JSONP(JSON with padding)
        • 1、概念
          • 2、举个栗子
          • 三、CORS
            • 1、概念
              • 2、实现原理
                • 3、代码
                  • 4、执行结果
                    • 5、代码解析
                    • 四、降域
                      • 1、iframe和网页不同源
                        • 2、降域
                        • 五、postMessage
                        相关产品与服务
                        云直播
                        云直播(Cloud Streaming Services,CSS)为您提供极速、稳定、专业的云端直播处理服务,根据业务的不同直播场景需求,云直播提供了标准直播、快直播、云导播台三种服务,分别针对大规模实时观看、超低延时直播、便捷云端导播的场景,配合腾讯云视立方·直播 SDK,为您提供一站式的音视频直播解决方案。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档