跨域二三事

2021/05/17 Javascript 共 2712 字,约 8 分钟

跨域访问到底是怎么一回事,为什么会有跨域?想要了解这个问题,需要我们回溯Web发展史,回溯Ajax发展史

发展史

最初的状态

页面的交互式一种“单击、等待”的交互模式。为了解决这个问题,带来更好的用户体验,诞生了一种能够向服务器请求额外的数据而无需卸载页面的技术:Ajax

Ajax(Async Javascript XML)的核心是借助XMLHttpRequest实现的,但是各浏览器厂商对XHR的底层实现并不一样,IE是通过ActiveX实现,在IE7以后,各浏览器才原生支持了XHR对象

XHR一级

XHR一级只是把已有的XHR对抗的实现细节描述了出来

  • 支持POST、GET请求
  • 支持了基础的能力
  • 支持abort取消请求
  • 上传Form需定义头部ContentType为application/x-www-form-urlencode
const xhr = new XMLHttpRequest()
const data = null
xhr.onreadystatechange = function() {
  // 0: 未初始化 1: 启动,以调用open方法,但尚未send 2: 发送,已调用send,但未接到响应
  // 3: 接收,已经接收到部分数据 4: 完成,已经接收到全部数据,并且可以在客户端使用
  if (xhr.readyState === 4) {
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      console.log(xhr.responseText)
    } else {
      console.log(`Req was unsuccessful: ${xhr.status}`)
    }
  }
}
xhr.open('get', 'example.txt', false) // false表示不同步发送
xhr.send(data)
// 在接收到相应之前可以取消异步请求
xhr.abort()

XHR二级

XHR二级进一步发展了XHR

  • 丰富了能力
  • 通过识别FormData对象,不用再手动传递Content-Type
  • 支持了timeout属性、onprogress事件
  • 引入了onload事件简化交互模型,替代onreadystatechange。响应接收完毕后会触发,不再需要检查readyState属性
// 设置1000ms后超时
xhr.timeout = 1000
xhr.ontimeout = function() {
  console.log('timeout')
}
xhr.onload = function(event) {
  if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
    console.log(xhr.responseText)
  } else {
    console.log(`Req was unsuccessful: ${xhr.status}`)
  }
}
// 需要在调用open之前调用pnprogress以保证正常执行
xhr.onprogress = function(event) {
  if (event/lengthComputable) {
    console.log(`Received ${event.position} of ${event.totalSize} bytes`)
  }
}

CORS(Cross-Origin Reqsource Sharing)

通过XHR实现Ajax通信的一个主要限制是跨域安全策略。XHR只能访问与包含它的页面同域名的资源。

实现合理的跨域请求对于开发某些浏览器应用也是至关重要的,于是有了CORS

CORS背后的思想是:通过约定自定义头部约束浏览器和服务器的通信,从而决定响应的成功与失败。当服务器返回的头部字段不匹配,此时浏览器会触发错误阻断接下来的通信。

OPTIONS

CORS访问出现繁荣,出于安全行考虑,CORS通过一种叫做Preflighted Requests的透明服务验证机制支持开发人员使用自定的头部、GET和POST以外的方法、以及不同类型的主体内容进行通信。

主要有以下头部字段参与验证:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers
  • Access-Control-Allow-Credentials

默认情况下,跨域请求不提供凭据(cookie、HTTP认证及客户端SSL证明等),需要显示指定witdhCredentials=true

const xhr = new XMLHttpRequest()
// 判断XHR是否支持CORS
const isXHRSupportCORS = 'withCrendentials' in xhr

其他跨域技术

图像Ping

image的src不受同源安全策略限制,但是有以下限制:

  • 支持之GET请求
  • 只能单向通信,无法访问服务器的响应文本
  • 一般用于埋点事件发送
  • 我们可以通过响应返回的image的图片大小,来约定响应的code值
const img = new Image()
img.onload = img.onerror = function() {
  console.log('done')
}
img.src = '<URL>'

JSONP

JSONP的原理是利用script src没有同源安全策略限制的特性,通过指定回调函数,请求一个script来动态加载其他域中的代码。 但这里有一个比较致命的缺点,即你必须完全信任你所请求的域下资源,防止在响应中夹带一些恶意的代码

const script = document.createElement('script')
script.src = 'http://freegeoip.net/json/?callback=handleRes'
document.body.insertBefore(script, document.body.firstChild)

handleRes({
  "name": "hola"
})

服务器推送

参考:WebSockets和EvnentSource简单对比&基本使用

跨域安全性问题

CSRF(Cross-Site Request Forgery)

指未被授权的系统却有权限访问某个资源的情况,主要发生场景是资源无请求限制或者发生请求伪造

有效手段:

  • 通过SSL连接来访问可以通过XHR请求的资源
  • XHR请求的每一次都需要附带经过算法计算得到的验证码。签名机制

以下方式效果不大:

  • 使用POST而不是GET – 很容易伪造
  • 检查来源URL是否可信 – 来源记录容易伪造
  • 基于cookie信息进行验证 – 容易伪造

[1]

Search

    Table of Contents