为什么会跨域
为了保证用户的信息安全,1995 年 Netscape 公司引入同源策略。当 协议(http、https 等)、端口号、域名(包括子域)不同时,就会违反同源策略。
违反同源策略会受到以下限制:
- 无法读取缓存数据,cookie、localstorage、indexDB
- DOM 无法获得
- ajax 无法发送
几种跨域请求方法
cors
通过在服务端设置,,如 access-control-allow-origin: _.qq.com ,cors 返回头,使得服务端可以接收来自 _.qq.com 的跨域请求。同时,如果想要跨端传递 cookie,则需要设置 credentials: true。
cors 预检
请求发出前,先发出 options 请求,去判 断 cors 是不是支持。以下请求会触发 cors 预检:
- 除 get、post、head 以外的请求方法;
- 在请求中的 xhr 对象注册了事件监听器;
- 人为设置了除安全部首字段以外的请求头,安全部首字段列表:
- content-type(值仅限 text/plain,multipart/form-data,application/x-www-form-urlencoded)
- accept
- accept-language
- content-language
- width
- 请求中没有 readstream 对象
在 axios 跨域时遇到过,跨域请求前,axios 先发出 options cors 预检请求,由于服务端没有支持 options 请求方法,导致预检失败。后来研究才知道,axios 默认的 content-type 是 application/text-json,有两种解决方案:
- 服务端支持 options 方法
- 显式设置请求头的 content-type 为 application/x-www-form-urlencoded
iframe 跨域(仅限于 子域名不一致,根域名一致的情景)
动态创建一个 iframe 标签,iframe 中设置 document.domain 为服务端和 客户端根域一致。设置 iframe 的 onload 回调,调用 iframe 的 request 方法向跨域服务发起请求。
const proxyRequest = (options) => {
return new Promise((resolve, reject) => {
const iframe = document.createElement('iframe');
iframe.src = 'demo.qq.com/proxy.html';
iframe.onLoad = () => {
iframe.contentWindow.request(options,(res) => {
resolve(res);
iframe.parentNode.removeChild(iframe);
})
}
document.body.appendChild(iframe);
})
}
//demo.qq.com/proxy.html
document.domain = 'qq.com'
window.request = (options,cb) => {
//...
}
jsonp
利用 script 标签 src 属性,引入一段 js 代码,被一个预先定义的函数名包裹着数据,如 cb({data: demoData}),执行完就可以获取里面的 data ;