innerWang / blogs

1 stars 0 forks source link

简要介绍跨域、JSONP以及CORS #11

Open innerWang opened 5 years ago

innerWang commented 5 years ago

1. 同源策略

浏览器出于安全的考虑,当页面向非本域(同协议,同域名,同端口)的接口请求数据时,无法读写对方的资源,这些限制会使合理的用途受到影响,因为就需要跨域,绕过上述限制获取数据。需要注意的是,同源策略是浏览器的限制,换个场景不一定存在,比如微信浏览器,服务端发请求等。

2. JSONP(JSON with padding)实现跨域

JSONP是一种通过加载<script>标签绕过同源策略来请求数据的javascript模式。

HTML的<script>标签可以调用服务器提供的js脚本,使用<script>标签向某个链接发送请求获取数据时,服务器返回的是JSON格式的数据,直接作为js去执行必然会报错。则我们可以提前在页面声明一个函数,并将函数名通过接口传参的方式传递给后台,当后台解析url的pathname得到函数名时使用 “函数名(原始数据)” 的形式发送给前端,前端在收到数据时则可以直接执行该函数,在函数内部处理原始数据即可。

/* req.js */
<script>
function parseResponse(ret){
   console.log(ret);
}
</script>
<script type="application/javascript"
        src="http://server.example.com/Users/student.json?callback=parseResponse">
</script>

3. CORS(Cross-Origin Resource Sharing) 实现跨域

cors 是一种 ajax 跨域请求资源的方式,支持现代浏览器及IE10以上,需要后端支持才可实现。cors 的请求分为简单请求(simple request)和非简单请求(not-so-simple request)两类。在此主要介绍简单请求的实现方式。

在向某一个接口发送 ajax请求数据时,浏览器检测到该接口不属于本域,则会在请求头上添加 Origin字段表明本次请求来自哪个源。

Origin: http://localhost:8080

服务端在接收到请求后会进行处理,确定接收请求则会在响应报文中设置响应头的字段:

Access-Control-Allow-Origin: http://localhost:8080

浏览器在收到响应后会判断响应头的上述 ACAO 字段是否包含 Origin的值,若包含,则处理响应,可成功获取响应数据;若不包含,浏览器会直接驳回,此时我们无法拿到响应数据。

4. 使用 window.postMessage() 方法实现跨域

window.postMessage()可实现如 页面与其弹出窗口(使用了window.open()) 或 页面与其嵌入的iframe 等窗口之间的跨域通信。该方法被调用时,会在所有页面脚本执行完毕后向目标窗口派发一个 MessageEvent 事件。

  targetWindow.postMessage(message, targetOrigin, [transfer])

接收方则可以通过监听message事件,检查事件的 origin/source属性,判断来源可信,则可以进行处理。 在此需要关注MessageEvent的几个属性:

示例如下:

//pageA
<div class="parent">
    pageA
    <button class="open">新建窗口</button>
    <button class="send">发送消息</button>
</div>
<script>
    let btnOpen = document.querySelector('.open')
    let btnSend = document.querySelector('.send')

    let childWindow = null
    // 打开pageB
    btnOpen.addEventListener('click',()=>{
        childWindow = window.open('http://localhost:3002/index.html')
    })
   //给PageB 发送消息
    btnSend.addEventListener('click',()=>{
        childWindow.postMessage('hello child','http://localhost:3002')
    })
   //监听messageEvent
    window.addEventListener('message',(e)=>{
        console.log(e)
        if(e.origin != "http://localhost:3002"){
          console.log('not valid origin')
          return
        }
        console.log('get message from child: '+ e.data)
    })
</script>
//pageB
<div class="parent">
    pageB
</div>

<script>
 window.addEventListener('message',(e)=>{
   console.log(e)
   if(e.origin != "http://localhost:3000"){
     console.log('not valid origin')
     return
   }
   console.log('get message: '+ e.data)
   e.source.postMessage('hello parent','http://localhost:3000')
 })
</script>