美文网首页
跨域的几种实现方式与原理

跨域的几种实现方式与原理

作者: 缺月楼 | 来源:发表于2019-04-24 19:29 被阅读0次

在实际工作中,遇到前后端进行数据交互,经常会碰到请求跨域,今天就来探讨一下什么是跨域以及跨域的几种实现方式!

什么是跨域

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

image.png
本域指的是?

如:
http://jirengu.com/a/b.jshttp://jirengu.com/index.php (同源)

不同源的例子(跨域):
http://jirengu.com/main.jshttps://jirengu.com/a.php (协议不同)
http://jirengu.com/main.jshttp://bbs.jirengu.com/a.php (域名不同,域名必须完全相同才可以)
http://jiengu.com/main.jshttp://jirengu.com:8080/a.php (端口不同,第一个是80)

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

请求跨域了,那么请求到底发出去没有?

看一下代码就会明白
HTML

HTML
<h1>饥人谷</h1>
<script>
  var xhr = new XMLHttpRequest()
  xhr.open('GET','http://127.0.0.1:8080/getWeather', true)
  xhr.send()
  xhr.onload = function(){
    console.log(xhr.responseText)
  }
</script>

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':
      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)

当前域名

当前域名
AJAX请求数据 是
请求域名
请求结果

跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。


状态显示已经成功 image.png

你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax就不会?因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。

跨域解决方案

jsonp

JSONP是通过script 标签加载数据的方式去获取数据当做 JS代码来执行 提前在页面上声明一个函数,函数名通过接口传参的方式传给后台,后台解析到函数名后在原始数据上「包裹」这个函数名,发送给前端。换句话说,JSONP需要对应接口的后端的配合才能实现。
JSONPAJAX对比
JSONPAJAX相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求)
JSONP优缺点
JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。

实现方式

HTMLscript标签可以加载其他域下的js,比如我们经常引入一个其他域下线上cdnjQuery。那如何利用这个特性实现从其他域下获取数据呢?

可以先这样试试:

<script src="http://api.jirengu.com/weather.php"></script>

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

这样试试:

<script src="http://api.jirengu.com/weather.php?callback=showData"></script>

这个请求到达后端后,后端会去解析callback这个参数获取到字符串showData,在发送数据做如下处理:

之前后端返回数据:{"city": "hangzhou", "weather": "晴天"} 现在后端返回数据showData({"city": "hangzhou", "weather": "晴天"})前端script标签在加载数据后会把 「showData({“city”: “hangzhou”, “weather”: “晴天”})」做为js来执行,这实际上就是调用showData这个函数,同时参数是 {“city”: “hangzhou”, “weather”: “晴天”}。 用户只需要在加载提前在页面定义好showData这个全局函数,在函数内部处理参数即可。

<script>
function showData(ret){
console.log(ret);
}
</script>
<script src="http://api.jirengu.com/weather.php?callback=showData"></script>

具体流程如下

  • 声明一个回调函数,其函数名(如showData)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)。

  • 创建一个 <script>标签,把那个跨域的API数据接口地址,赋值给scriptsrc,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=showData)。

  • 服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是showData,它准备好的数据是 show({"city": "hangzhou", "weather": "晴天"})

  • 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(showData),对返回的数据进行操作。

以上就是JSONP(JSON with padding)

CORS

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

<!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>

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: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)
访问成功

浏览器会进行比较,如果一致就允许访问,否则就拒绝!
CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。

浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。

服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。
res.setHeader('Access-Control-Allow-Origin','*')(所有网站都可以访问资源)

降域

原理就是 设置另个域名之间相同的后缀 实现降域


image.png
<html>
<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>使用降域实现跨域</h1>
  <div class="main">
    <input type="text" placeholder="http://a.jrg.com:8080/a.html">
  </div>

  <iframe src="http://b.jrg.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>

postMessage

postMessageHTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:

  • 页面和其打开的新窗口的数据传递

  • 多窗口之间消息传递

  • 页面与嵌套的iframe消息传递

上面三个场景的跨域数据传递

postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
message: 将要发送到其他 window的数据。

targetOrigin:通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串 *(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。

transfer(可选):是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

// a.html

  
<iframe src="http://localhost:4000/b.html" frameborder="0"id="frame"onload="load()"></iframe> //等它加载完触发一个事件  
//内嵌在http://localhost:3000/a.html
<script>
function load(){
let frame =document.getElementById('frame')
frame.contentWindow.postMessage('我爱你','http://localhost:4000')
//发送数据
window.onmessage = function(e){
//接受返回数据
console.log(e.data)
//我不爱你    
}    
}
  </script>

参考资料:
前端工匠
饥人谷

相关文章

  • #hello,JS:15 同源策略 & 跨域(JSONP)

    跨域有几种常见的方式?你有没有跨域使用的经验? 方式: 使用jsonp实现跨域?使用cors实现跨域?浏览器另类的...

  • 跨域的几种实现方式与原理

    在实际工作中,遇到前后端进行数据交互,经常会碰到请求跨域,今天就来探讨一下什么是跨域以及跨域的几种实现方式! 什么...

  • 跨域问题详解分析

    参考文档 CORS详解 跨域资源共享 CORS 详解 js中几种实用的跨域方法原理详解 跨域的那些事儿 跨域与跨域...

  • JSONP跨域

    JSONP原理及应用 之前的文章中简单介绍过前端可以实现的跨域方式,这次介绍一种更为常用的跨域方式,但这种跨域方式...

  • js跨域问题

    来源 javascript中实现跨域的方式总结 第一种方式:jsonp请求;jsonp的原理是利用 标签的跨域特性...

  • Ajax 请求和跨域

    跨域的几种方式: cors方式 cross-orign-resource-shareing(跨域)参考:http:...

  • 跨域

    有以下几个问题 什么是同源策略 什么是跨域?跨域有几种实现形式 JSONP 的原理是什么 CORS是什么 我们先描...

  • 跨域的几种实现方式

    JSONP JSONP 是JSON with padding(填充式JSON 或参数式JSON)的简写;JSONP...

  • 跨域的几种实现方式

    概念1:同源策略 同源策略,Same-origin policy,浏览器处于安全考虑,只允许与本域下的接口交互。不...

  • 跨域的几种实现方式

    JSONP CORS 降域 对于页面中使用iframe嵌套另外的网站时,如果符合同源策略,就可以通过window....

网友评论

      本文标题:跨域的几种实现方式与原理

      本文链接:https://www.haomeiwen.com/subject/dkmvgqtx.html