前端跨域通信原理及示例

参考资源:
前端跨域通信的几种方式

前端跨域通信原理及示例

同源策略及限制

同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。

所谓同源是指:域名,协议,端口 相同。

cookie,localstorage,dom 没办法相互获取 ,ajax 请求也不能

前后端的通信方式

  • Ajax - 通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新 - 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术 - 同源下使用的通信方式

  • WebSocket - 不限制同源策略 - 基于 TCP 的一种新的网络协议 - 实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。

  • CORS - 支持跨域通信也支持同源通信 - CORS 机制让 web 服务器能跨站访问控制 - 简单请求 - 浏览器直接发送 CORS 跨域请求,并在 header 信息中增加一个 Origin 字段,表明这是一个跨域的请求。 - 非简单请求 - 在正式通信前进行一次 Http 查询请求,又称预检请求

跨域通信

  • JSONP
  • HASH
  • postMessage
  • WebSocket
  • CORS

JSONP

跨域原理

利用 <script src=””></script> 中的 src 的地址可以跨域,动态的构造 script 标签,以实现跨域数据访问。

前端通过 script 标签以发送请求,

1
<scirpt src="http://www.abc.com/?data=name&callback=callback_fn" charset="utf-8"></scirpt>

服务端返回的也是 script 标签,callback 就是方法名

1
2
3
4
function callback_fn(data) {
console.log("callback_fn")
console.log(data)
}

完整的 jsonp 封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
* [function 获取一个随机的5位字符串]
* @param {[type]} prefix [description]
* @return {[type]} [description]
*/
util.getName = function(prefix) {
return (
prefix +
Math.random()
.toString(36)
.replace(/[^a-z]+/g, "")
.substr(0, 5)
)
}

/**
* [function 判断是否为函数]
* @param {[type]} source [description]
* @return {[type]} [description]
*/
util.isFunction = function(source) {
return "[object Function]" === Object.prototype.toString.call(source)
}

/**
* [function jsonp]
* @param {[type]} url [请求地址]
* @param {[type]} onsucess [成功的回调]
* @param {[type]} onerror [错误处理方法]
* @param {[type]} charset [字符集]
* @return {[type]} [description]
*/
util.jsonp = function(url, onsuccess, onerror, charset) {
var callbackName = util.getName("tt_player")
window[callbackName] = function() {
if (onsuccess && util.isFunction(onsuccess)) {
onsuccess(arguments[0])
}
}
var script = util.createScript(url + "&callback=" + callbackName, charset)
script.onload = script.onreadystatechange = function() {
if (!script.readyState || /loaded|complete/.test(script.readyState)) {
script.onload = script.onreadystatechange = null
// 移除该script的 DOM 对象
if (script.parentNode) {
script.parentNode.removeChild(script)
}
// 删除函数或变量
window[callbackName] = null
}
}
script.onerror = function() {
if (onerror && util.isFunction(onerror)) {
onerror()
}
}
document.getElementsByTagName("head")[0].appendChild(script)
}

Hash

url# 后面的内容就叫 HashHash 的改变,页面不会刷新。这就是用 Hash 做跨域通信的基本原理。

补充:url? 后面的内容叫 SearchSearch 的改变,会导致页面刷新,因此不能做跨域通信。

使用举例:

场景:我的页面 A 通过 iframeframe 嵌入了跨域的页面 B

现在,我这个 A 页面想给 B 页面发消息,怎么操作呢?

(1)首先,在我的 A 页面中:

1
2
3
//伪代码
var B = document.getElementsByTagName("iframe")
B.src = B.src + "#" + "jsonString" //我们可以把JS 对象,通过 JSON.stringify()方法转成 json字符串,发给 B

(2)然后,在 B 页面中:

1
2
3
4
5
// B中的伪代码
window.onhashchange = function() {
//通过onhashchange方法监听,url中的 hash 是否发生变化
var data = window.location.hash
}

postMessage()方法

H5 中新增的 postMessage() 方法,可以用来做跨域通信。既然是 H5 中新增的,那就一定要提到。

场景:窗口 A (http:A.com) 向跨域的窗口 B (http:B.com)发送信息。步骤如下。

(1)在 A 窗口中操作如下:向 B 窗口发送数据:

1
2
// 窗口A(http:A.com)向跨域的窗口B(http:B.com)发送信息
Bwindow.postMessage("data", "http://B.com") //这里强调的是B窗口里的window对象

WebSocket

WebSocket 的用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var ws = new WebSocket("wss://echo.websocket.org") //创建WebSocket的对象。参数可以是 ws 或 wss,后者表示加密。

//把请求发出去
ws.onopen = function(evt) {
console.log("Connection open ...")
ws.send("Hello WebSockets!")
}

//对方发消息过来时,我接收
ws.onmessage = function(evt) {
console.log("Received Message: ", evt.data)
ws.close()
}

//关闭连接
ws.onclose = function(evt) {
console.log("Connection closed.")
}

(2)在 B 窗口中操作如下:

1
2
3
4
5
6
7
8
9
10
11
// 在窗口B中监听 message 事件
Awindow.addEventListener(
"message",
function(event) {
//这里强调的是A窗口里的window对象
console.log(event.origin) //获取 :url。这里指:http://A.com
console.log(event.source) //获取:A window对象
console.log(event.data) //获取传过来的数据
},
false
)

CORS

CORS 可以理解成是既可以同步、也可以异步*的 Ajax

fetch 是一个比较新的 API,用来实现 CORS 通信。用法如下:

1
2
3
4
5
6
7
8
9
10
// url(必选),options(可选)
fetch("/some/url/", {
method: "get"
})
.then(function(response) {
//类似于 ES6中的promise
})
.catch(function(err) {
// 出错了,等价于 then 的第二个参数,但这样更好用更直观
})

CORS 为什么能实现跨域通信?浏览器会拦截 Ajax如果Ajax 是跨域的,会在 Http头部增加 origin

CORSJSONP 比较

CORSJSONP 都是为了使 web 浏览器能够跨源请求,使用目的相同,但是比 JSONP 更强大。JSONP 只支持GET 请求,而 CORS 支持所有类型的 HTTP 请求,不过JSONP 的优势在于支持老式浏览器以及可以向不支持 CORS 的网站跨源请求。