跨域访问到底是怎么一回事,为什么会有跨域?想要了解这个问题,需要我们回溯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
信息进行验证 – 容易伪造