主要通过几个问题来理解下跨域相关的知识。

📣什么是跨域?

  cors,全称“跨域资源共享”(Cross-origin resource sharing)

📣跨域如何产生?

  跨域的产生是出于对安全方面的考虑,在不同源之间不能互相访问。

  首先说下这么做的优点,再说下跨域的产生是否是应该的,逆推,如果不同的域能够相互访问,那么其域名A能够获取域名B资源,能够操作域名B的dom元素,改变其表现形式等等,那么无安全可言,而这问题早期被网景公司解决,提出了❓一个安全策略,即同源策略,那么说明跨域的产生是应该的。

  web应用或者客户端应用都是建立在同源策略基础上,所以其是不同源不能相互访问,即产生了跨域。

📣如何避免跨域?

  方法很多种,无非就是从前、后端来处理,今天就先罗列几种常用的,日后来补充下。

  • jsonp

  这是比较简单的方式,而其逻辑也是很常见的,由于script标签不受同源策略的限制,所以在需要用到的地方动态加载script,其src即为接口地址,而动态加载script标签是get请求.所以jsonp只能支持get请求
  那么简单一段伪代码以示之:

1
2
3
4
5
6
let script = document.createElement('script');
script.src = 'https://api.myjson.com/bins/egghd?callback=callback';
document.body.appendChild(script);
let callback = function(obj){
// 此处obj即为得到的数据...
}

  由此可见,jsonp的方式是在后端包装了一层function以json或string的形式返回,前端采用回调的方式得到它的值。

  • CORS

  此种方式主要在于服务端的处理,用于允许对某些域的访问,主要通过添加响应头信息的Access-Control-Allow-Origin字段为指定的域名或者”“(接收任意请求),另外比较重要的一点是在一些登录注册类信息产生cookie,其Access-Control-Allow-Origin字段即不能设置为”“,需要是指定的域名,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传。
  那么简单一段伪代码以示之:

1
2
3
4
5
6
7
8
9
10
11
app.get('/queryUser', function(req, res) {
res.header("Access-Control-Allow-Origin", "*");
res.json({ _id: 111, name: "amos", sex:"male" });
});
// 或者
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
next();
})

  以上仅为express的相关处理,更方便的处理作为中间件来对每个请求进行返回响应头。

  • 借助服务器跨域

  借助服务端跨域,为何为借助,其实本质上还是通过发出ajax请求获取数据,只不过在这边做了两次请求,一次请求同域名下的接口,其次服务端在得到接口情况下,再去请求不同域的接口,从而返回给前端,在此服务端充当了中转站的作用,此举实乃曲线救国呀!虽然其解决了跨域的问题,但其存在很大的不稳定性,当不同域的接口的请求参数或返回的结果的变动,将会使得该跨域请求变得难以维护。
  那么简单一段伪代码以示之:

1
2
3
4
5
6
7
8
9
10
前端:
axois.get('localhost:8000/getUser').then(res => {
// 返回用户结果
})

后端:
app.get('/getUser', async (req, res, next) => {
let user = await axios.get('locahost:5000'); // 其他域获取的结果
res.json(user);
})

  以上作为简单的模拟前后端交互,通过服务端的请求转发而获取到的数据返回给前端。

  • postmessage跨域

originWindow.postMessage(message, targetOrigin);

  • originWindow:要发起请求的窗口引用
  • message: 要发送出去的消息,字符串或者对象
  • targetOrigin: 指定哪些窗口能够收到消息,其值可以是字符串*(表示无限制)或者一个确切的URI
    只有目标窗口的协议、主机地址或端口这三者全部匹配才会发送消息。

  此种方式为h5新增api,window.postMessage()方法被调用时,会在所有页面脚本执行完毕之后向目标窗口派发一个 MessageEvent 消息。
  那么简单一段伪代码以示之:

1
2
3
4
5
6
7
8
9
10
11
// b页面 b.com
<iframe src="http://a.com/index.html"></iframe>
<script>
window.frames[0].postMessage("a页面",'http://a.com');
</script>


// a页面 a.com/index.html
<script>
window.parent.postMessage("b页面",'http://b.com');
</script>

  通过iframe的方式将a页面加载到b页面中,此时b页面能过和a页面通信,b页面也能获取到父页面的数据信息,包含与被包含的关系。

  还有其他通过iframe跨域的方式以后补上来吧,其原理也类似postmessage。

小节

  跨域的方式多种多样,无一不是根据同源策略的规则而制造的解决方案,或者曲线救国。

  最后,引用我高中物理老师的话:“这虽然是老生常谈的问题了,但还是很重要滴,考试(‘面试’)经常会考,希望大家(‘我自己’)一定要记录咯!”