说一说对跨域的认识
# 为什么会跨域
同源策略,它规定了协议号-域名-端口号这三者必须都相同才符合同源策略。
如下:
如有一个不相同,就会出现跨域问题,不符合同源策略导致的后果有
- LocalStorge、SessionStorge、Cookie等浏览器内存无法跨域访问
- DOM节点无法跨域操作
- Ajax请求无法跨域请求
注意
一个IP是可以注册多个不同域名的,也就是多个域名可能指向同一个IP,即使是这样,他们也不符合同源策略
# 跨域的时机
结论
请求发出去到后端,后端返回数据,在浏览器接收后端数据时被浏览器的跨域报错拦下来
# 解决跨域的方案
# 1. JSONP
前面咱们说了,因为浏览器同源策略的存在,导致存在跨域问题,那有没有不受跨域问题所束缚的东西呢?其实是有的,以下这三个标签加载资源路径是不受束缚的
- script标签:<script src="加载资源路径"></script>
- link标签:<link herf="加载资源路径"></link>
- img标签:<img src="加载资源路径"></img>
而JSONP就是利用了script的src加载不受束缚
,从而可以拥有从不同的域拿到数据的能力。但是JSONP需要前端后端配合
,才能实现最终的跨域获取数据。
JSONP通俗点说就是:利用script的src去发送请求,将一个方法名callback传给后端,后端拿到这个方法名,将所需数据,通过字符串拼接成新的字符串callback(所需数据),并发送到前端,前端接收到这个字符串之后,就会自动执行方法callback(所需数据)。老规矩,先上图,再上代码。
后端代码:
// index.js http://127.0.0.1:8000
const http = require('http');
const urllib = require('url');
const port = 8000;
http.createServer(function (req, res) {
const { query } = urllib.parse(req.url, true);
if (query && query.callback) {
const { name, age, callback } = query
const person = `${name}今年${age}岁啦!!!`
const str = `${callback}(${JSON.stringify(person)})` // 拼成callback(data)
res.end(str);
} else {
res.end(JSON.stringify('没东西啊你'));
}
}).listen(port, function () {
console.log('server is listening on port ' + port);
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
前端代码:
// index.html http://127.0.0.1:5500/index.html
const jsonp = (url, params, cbName) => {
return new Promise((resolve, reject) => {
const script = document.createElement('script')
window[cbName] = (data) => {
resolve(data)
document.body.removeChild(script)
}
params = { ...params, callback: cbName }
const arr = Object.keys(params).map(key => `${key}=${params[key]}`)
script.src = `${url}?${arr.join('&')}`
document.body.appendChild(script)
})
}
jsonp('http://127.0.0.1:8000', { name: 'xxx', age: 13 }, 'callback').then(data => {
console.log(data) // xxx今年13岁啦!!!
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2. Cors
Cors,全称是Cross-Origin Resource Sharing,意思是跨域资源共享
,Cors一般是由后端来开启
的,一旦开启,前端就可以跨域访问后端。
为什么后端开启Cors,前端就能跨域请求后端呢?我的理解是:前端跨域访问到后端,后端开启Cors,发送Access-Control-Allow-Origin: 域名 字段到前端(其实不止一个),前端浏览器判断Access-Control-Allow-Origin的域名如果跟前端域名一样,浏览器就不会实行跨域拦截,从而解决跨域问题。
后端代码:
// index.js http://127.0.0.1:8000
const http = require('http');
const urllib = require('url');
const port = 8000;
http.createServer(function (req, res) {
// 开启Cors
res.writeHead(200, {
//设置允许跨域的域名,也可设置*允许所有域名
'Access-Control-Allow-Origin': 'http://127.0.0.1:5500',
//跨域允许的请求方法,也可设置*允许所有方法
"Access-Control-Allow-Methods": "DELETE,PUT,POST,GET,OPTIONS",
//允许的header类型
'Access-Control-Allow-Headers': 'Content-Type'
})
const { query: { name, age } } = urllib.parse(req.url, true);
res.end(`${name}今年${age}岁啦!!!`);
}).listen(port, function () {
console.log('server is listening on port ' + port);
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 3. Node接口代理
还是回到同源策略,同源策略它只是浏览器的一个策略而已,它是限制不到后端的,也就是前端-后端会被同源策略限制
,但是后端-后端则不会被限制
,所以可以通过Node接口代理
,先访问已设置Cors的后端1,再让后端1去访问后端2获取数据到后端1,后端1再把数据传到前端
# 4. Nginx
其实Nginx跟Node接口代理是一个道理,只不过Nginx就不需要我们自己去搭建一个中间服务
将nginx目录下的nginx.conf修改如下:
server{
listen 8888;
server_name 127.0.0.1;
location /{
proxy_pass 127.0.0.1:8000;
}
}
2
3
4
5
6
7
8
# 5. postMessage
场景:http://127.0.0.1:5500/index.html页面中使用了iframe标签内嵌了一个http://127.0.0.1:5555/index.html的页面
虽然这两个页面存在于一个页面中,但是需要iframe标签来嵌套才行,这两个页面之间是无法进行通信的,因为他们端口号不同,根据同源策略,他们之间存在跨域问题
那应该怎么办呢?使用postMessage可以使这两个页面进行通信
// http:127.0.0.1:5500/index.html
<body>
<iframe src="http://127.0.0.1:5555/index.html" id="frame"></iframe>
</body>
<script>
document.getElementById('frame').onload = function () {
this.contentWindow.postMessage({ name: '林三心', age: 23 }, 'http://127.0.0.1:5555')
window.onmessage = function (e) {
console.log(e.data) // 通信内容
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
// http://127.0.0.1:5555/index.html
<script>
window.onmessage = function (e) {
const { data: { name, age }, origin } = e
e.source.postMessage(`${name}今年${age}岁啦!!!`, origin)
}
</script>
2
3
4
5
6
7
8
# 6. document.domain && iframe
场景:a.sanxin.com/index.html 与 b.sanxin.com/index.html之间的通信
其实上面这两个正常情况下是无法通信的,因为他们的域名不相同,属于跨域通信
那怎么办呢?其实他们有一个共同点,那就是他们的二级域名都是sanxin.com,这使得他们可以通过document.domain && iframe的方式来通信
// http://127.0.0.1:5500/index.html
<body>
<iframe src="http://127.0.0.1:5555/index.html" id="frame"></iframe>
</body>
<script>
document.domain = '127.0.0.1'
document.getElementById('frame').onload = function () {
console.log(this.contentWindow.data) // 通信内容
}
</script>
// http://127.0.0.1:5555/index.html
<script>
// window.name="通信内容"
document.domain = '127.0.0.1'
var data = '通信内容';
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 7. WebSocket
WebSocket是一种协议(跟http同级,都是协议),并且他可以进行跨域通信。