incuisting/blogs

跨域的几种实现方法

Opened this issue · 0 comments

CORS

实现CORS需要浏览器和服务器的共同协作。对于前端开发者来说,CORS通信与同源的AJAX在代码方面没有差别。浏览器一旦发现AJAX请求跨源,就会自动添加附加一些请求头信息,有时候还会多出一次附加的请求,但用户不会察觉。
因此,CORS通信的关键就在于后端代码的实现,只要服务器实现了CORS接口,就可以跨源通信。

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

  • 简单请求
    • 请求方法是以下三种方法之一:
      • HEAD
      • GET
      • POST
    • HTTP的头信息不超出以下几种字段:
      • Accept
      • Accept-Language
      • Content-Language
      • Last-Event-ID
      • Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
  • 非简单请求
    • 凡是不同时满足上面两个条件,就属于非简单请求

两种请求浏览器对他们的处理方式也不一样

简单请求处理流程

(发起AJAX请求) ==> (浏览器发现请求为跨源AJAX简单请求)==>(浏览器自动在头信息里添加Origin字段) ==>(服务器根据收到的Origin字段来决定是否同意这次请求)==> (浏览器得到回应,根据返回的头信息没有包含Access-Control-Allow-Origin字段判断本次CORS请求是否成功)

Origin字段: 用来说明,本次请求来自哪个源(协议 + 域名 + 端口)

非简单请求处理流程

(向自动发出预检请求)==> (服务器回应预检请求)==> (服务器同意则会返回一个带Access-Control-Allow-Origin头信息的HTTP回应) ==> (浏览器判断预检请求是否被允许) ==>(如果预检请求被通过,以后每次的非简单请求,就都和简单请求一样)


代码示例:

** 简单请求 **

  var url = 'http://api.a.com/cors';// 在本站地址http://localhost:8080
  var xhr = new XMLHttpRequest();
  xhr.open('get',url,true);
  xhr.send()

非简单请求

  var url = 'http://api.a.com/cors';// 在本站地址http://localhost:8080
  var xhr = new XMLHttpRequest();
  xhr.open('put',url,true); //发起的非GET POST HEAD 的请求
  xhr.send()

参考文章

JSONP

利用HTML中script标签可以引入其他域下的js的特性,来实现跨域访问接口。后端配合返回如下格式的数据:

dosomething({
  ’aa‘:'bb',
  ‘cc’:{....}
})

因为是script标签的请求,所以返回的数据会作为javascript去执行。那么我们只用再定义一个dosomething函数就可以了,他的参数就是返回的json数据。

<script type="text/javascript">
   function dosomething(json){
     //处理JSONP放回的数据
   }
</script>
  <script src="http://api.a.com/cors?callback=dosomething"></script>

修改document.domain

在页面 http://b.example.com/a.html 中设置document.domain:

  <div class='main' >
    <input type="text">
  </div>
<iframe  src="http://a.example.com/b.html"></iframe>
<script type="text/javascript">
    document.domain = 'example.com';//设置成主域
  document.querySelector('.main input').addEventListener('input',function(){
  console.log(this.value);
  window.frames[0].document.querySelector('input').value = this.value;//对调用的iframe进行dom操作
})
</script>

在页面 http://b.example.com/b.html 中也设置document.domain:

 <div class='main' >
    <input id='input' type="text">
  </div>
<script type="text/javascript">
document.domain = 'example.com';//设置成主域
     document.querySelector('#input').addEventListener('input',function(){
  window.parent.document.querySelector('input').value = this.value;//因为是用iframe调用了b.html所用用window.parent来获得父级页面
})
</script>

两个在同源下不同域的两个页面通过document.domain可以实现主域名一样下两个子域名网站做iframe嵌套的时候进行相互操作dom。

PostMassage

window.postMessage() 方法可以安全地实现跨源通信。

语法

示例

页面http://b.example.com/b.html 中设置PostMessage

 <div class='main' >
    <input type="text">
  </div>
<iframe  src="http://a.example.com/b.html"></iframe>
<script type="text/javascript">
  document.querySelector('.main input').addEventListener('input',function(){
  console.log(this.value);
  window.frames[0].postMessage(this.value.'');// * 表示没有指定域名,也可以把*指定成固定的域名
})
window.addEventListener('message',function(e){ //监听message事件,可以监听别人给他发的内容,然后拿出来使用
  document.querySelector('.main input').value = e.data
})
</script>

message事件还有如下属性:

安全问题

如果你不希望从其他网站接收message,那么就不要为message事件添加任何事件侦听器。 这是一个完全万无一失的方式来避免安全问题。如果你用了,那么在监听message事件的时候一定要做origin的验证。如下:

function receiveMessage(event)
{  if (event.origin !== "http://example.org")
    return;
}
window.addEventListener("message", receiveMessage, false);

这样你就可以保证你接受到的信息是来自你希望收到的网站

使用场景:

1.页面和其打开的新窗口的数据传递
2.多窗口之间消息传递
3.页面与嵌套的iframe消息传递