对于前端来说,跨域是基础工作之一。
同源策略
提到跨域,不得不先说同源策略。
同源策略是一种指浏览器的安全机制,只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。
本域是指:
- 同协议:都是http或者都是https
- 同域名: 如 http://test.com/a 和 http://test.com/b
- 同端口: 如都是3000端口
打个比方,淘宝域名的网站请求腾讯域名网站的资源,会失败,相反,亦同(马爸爸们怎么可能允许这种事情发生🙄)。
失败?在哪失败?没发出去?发出去了人家服务器没理你?还是理你了但是浏览器不给你看?
测一把,测试中的服务器端没有做任何域名校验。代码
结果是,代码发出去了,服务器也响应并返回了,浏览器中 NetWorks 请求的 Status Code也是200,但是无法看到响应体。
因而,是浏览器认为不安全,从而屏蔽了响应体。
跨域
再说跨域,那就是允许不同域的接口进行交互,就是POST、GET的url不是当前的网站,域名不同。(比如一个马爸爸在某些特殊情况下竟然允许另外一个马爸爸请求自己的资源🙃。。。)
跨域有几种方式:
- JSONP
- CORS
- 降域
- postMessage
JSONP
- 无论是静态页面、动态页面、还是web服务、WCR, ajax直接请求普通文件都存在跨域问题;
- 页面中调用 js 文件不受跨域的影响(凡是拥有 src 属性的标签都有跨域能力, e.g. img iframe );
- 那么,如果在远程服务器上设法把数据装进 js 格式的文件里,供客户端调用和进一步处理, 就可以通过web端跨域访问到这份数据;
- js 支持 JSON,在加载src文件时,会作为 js 来执行;
综上,在请求数据时,请求带一个 callback 参数,参数值为处理返回数据的函数的函数名,服务器动态生成JSON数据,将这个 callback 所带的函数名包裹住 JSON 数据,然后返回, 客户端加载则立即执行该 callback funciton, 从而对数据进行相应的处理。
这种非正式传输协议,就称作 JSONP (JSON with Padding) , 可以让网页从别的域名获取数据,并将 JSON 数据填充进 callback function 从而回调。
JSONP 步骤
- 定义 callback函数 cb;
- 创建script标签,src的地址为请求的url,最后增加参数 callback=cb;
- 服务端在收到请求后,解析参数,计算并生成 JSON 数据,输出 cb(JSON) 字符串;
- 客户端收到数据, 作为 js 执行,调用 cb 函数,服务器返回的 JSON 为 cb 的参数。
JSONP 实例
CORS
CORS 是指跨域资源共享(Cross-Origin Resource Sharing),是⼀种 ajax 跨域请求资源的⽅式,⽀持现代浏览器(IE 10+)。
CORS步骤:
- 使⽤XMLHttpRequest 发送请求时,发现该请求不符合同源策略,给该请求加⼀个 Origin请求头;
- 后台对请求的域名进行判断,在返回结果中加入相应的响应头 Access-Control-Allow-Origin;
- 浏览器判断该响应头中是否包含 Origin 的值,如果包含则不屏蔽响应体,文件继续执行,反之则屏蔽响应体,该报错报错,该跳过跳过。
CORS 实例
核心部分
res.header("Access-Control-Allow-Origin", "http://www.aaa.com:8080");
res.header("Access-Control-Allow-Origin", "*"); // accept all the domains
降域
对于主域相同但是子域不相同的一种解决方法。
降域步骤
- 比如两个域名分别是 a.test.com/a.html 以及 b.test.com/b.html;
- 在两个文件中分别加上 document.domain = "test.com";
- 在 a.html 中创建一个iframe,src 为 b.html , 控制其content Document,从而两个 js 文件就可以"交互"了。
降域 实例
postMessage
通常用于:
- 页面和其打开的新窗口的数据传递
- 多窗口之间数据传递
- 页面与其 iframe 数据传递
postMessage(data,origin) 方法接受两个参数:
- data: 要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。
- origin: 字符串,指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
window.postMessage() 方法被调用时,会在所有页面脚本执行完毕之后向目标窗口派发一个 MessageEvent.
postMessage 实例
核心部分
// a.test.html
const input = document.querySelector('input')
input.addEventListener('input', function () {
window.frames[0].postMessage(this.value, '*')
})
// 监听其它页面发送来的消息
window.addEventListener('message', function (e) {
input.value = e.data;
});
// b.test.html
const input = document.querySelector('input')
input.addEventListener('input', function () {
window.parent.postMessage(this.value, '*')
})
// 监听其它页面发送来的消息
window.addEventListener('message', function (e) {
input.value = e.data;
});
降域和postMessage嘛,我个人感觉是没有另外两种使用广泛,JSONP、CORS都是实打实的需要后端配合的,然而如果后端童鞋不鸟你,那...
综上所述,后端同样是前端的必备技能啊,666.....